With the help of herminator, few answers !

The zip statement creates a list of lists, where for each list, the first item is the group number and the other items are the students. That is why, to split the number from the students, you use groups.each do |index, *items| (then the number is in the index variable and the rest of the list is in the items variable.

In you case where numgroups == 1, zip still creates a list of lists, and there is still a number in front of it, so when you do groups.each do |*items|, the number is included in your items variable. So you should still do |index, *items| there too.

The second problem is that a return statement immediately exits the loop it is in. So when you loop through the groups, it returns the first group and stops. To get the result you want, you should have a results list, add each html string to that results list and then return that after the loop.