0fe01b8879c35beee8c9b2e9212a5b87

I would like to have a piece of code which splits an array in a fixed number of groups.

On a page I have 4 colums. I want to display an array of friends in 4 columns.
On http://skwpspace.com/2006/09/27/multi-column-lists-using-in_groups_of/ I found an example with in_groups_of, but this doesn't do the trick.

An array with 9 friends will result in 3 groups of 3 friends each. [1,2,3],[4,5,6],[7,8,9],[]
and 10 friends will result in 4 groups [1,2,3],[4,5,6],[7,8,9],[10]
And I wanted the results to be: 9: [1,2,3],[4,5],[6,7],[8,9] and [1,2,3],[4,5,6],[7,8],[9,10]

Is there an easy why of getting this?

1
2
3
4
5
6
7
8
9
10
<% col_size = (friends.size/4.0).ceil %>
<% friends.in_groups_of(col_size, false) do |group| %>
  <div class=‘list‘>
    <ul>
    <% group.each do |friend| %>
      <li><%= display friend %></li>
    <% end %>
    </ul>
  </div>
<% end %>

Refactorings

No refactoring yet !

5170ca260dbd2cdfd5a887a4dba7636f

Jeremy Weiskotten

November 28, 2007, November 28, 2007 16:21, permalink

No rating. Login to rate!

You seem to be specifying three different expected results for an array of 9 friends. Which is correct?

[1,2,3],[4,5,6],[7,8,9],[]
[1,2,3],[4,5],[6,7],[8,9]
[1,2,3],[4,5,6],[7,8],[9,10]

Any of these should be pretty easy to do, but the requirements aren't clear.

D16d53391068ff0830269149b060789d

Jason Dew

November 28, 2007, November 28, 2007 17:23, permalink

2 ratings. Login to rate!

here's my first shot at it

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Array

  def in_uniform_groups_of(group_count)
    answer = Array.new
    index = 0

    self.class.uniform_partition_sizes(self.size, group_count).each do |size|
      answer << self.slice( index, size )
      index += size
    end

    answer
  end

  def self.uniform_partition_sizes(size, partitions)
    answer = Array.new( partitions, size / partitions )

    index = 0
    total = (size / partitions) * partitions

    while total < size
      answer[index] += 1
      index = (index + 1) % partitions
      total += 1
    end

    answer
  end

end

puts (1..9).to_a.in_uniform_groups_of(4).inspect
puts (1..10).to_a.in_uniform_groups_of(4).inspect
0fe01b8879c35beee8c9b2e9212a5b87

JohnnyBusca

November 28, 2007, November 28, 2007 20:57, permalink

No rating. Login to rate!

The example shown above returned:
9 friends: [1,2,3],[4,5,6],[7,8,9],[]
10 friends: [1,2,3],[4,5,6],[7,8,9],[10]

and I wanted the following:
9 friends: [1,2,3],[4,5],[6,7],[8,9]
10 friends: [1,2,3],[4,5,6],[7,8],[9,10]

In the meantime I've come up with an helper method which works as a wanted.

1
2
3
4
5
6
7
def split_in_groups(values, cols=4) 
  cols.downto(1) do |col|
    group_size = (values.size / col.to_f).ceil - 1
    yield(group = values[0..group_size])
    values -= group
  end
end
5170ca260dbd2cdfd5a887a4dba7636f

Jeremy Weiskotten

November 28, 2007, November 28, 2007 22:20, permalink

No rating. Login to rate!

I misread your original post. Now I understand what you're expecting.

I'll post something in a bit.

5170ca260dbd2cdfd5a887a4dba7636f

Jeremy Weiskotten

November 28, 2007, November 28, 2007 23:11, permalink

1 rating. Login to rate!

I've added a columnize method to Array. The unit test confirms that 1-9 and 1-10 in 4 columns behave the way you expect. I think this will work for any other combo. I'm not sure what the behavior should be if there aren't enough items in the array to populate the given number of columns -- in this implementation the method will return an array with only as many columns as necessary, but it would be pretty easy to have it return empty or nil columns instead.

This should be more efficient than the helper you posted above, because it's not resizing the (potentially large) array as you do with "values -= group". It does all of its work with some simple arithmetic to calculate offset and column size for slicing out of the source array.

Please rate this refactoring if it helps!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Array
  def columnize(n_columns)
    columns = []
    
    remaining_size = self.size
    
    n_columns.times do |column_idx|
      column_size = (remaining_size.to_f / (n_columns - column_idx)).ceil
      break if column_size == 0
      columns << self.slice(self.size - remaining_size, column_size)
      remaining_size -= column_size
    end

    columns
  end
end

class ColumnizeTest < Test::Unit::TestCase    
  def test_columnize_9
    assert_equal [[1,2,3],[4,5],[6,7],[8,9]], (1..9).to_a.columnize(4)
  end
  
  def test_columnize_10
    assert_equal [[1,2,3],[4,5,6],[7,8],[9,10]], (1..10).to_a.columnize(4)
  end
end

Your refactoring





Format Copy from initial code

or Cancel