Tricky ruby array grouping method


#1

I want to define a method that performs the following operation:

Given an array: array = [a, b, c, d, e, f, g, h, i]
My imaginary method (let’s call it nest_by) would produce these
results
array.nest_by(2)
=> [[a, b], [c, d], [d, e], [f, g], [h, i]]
array.nest_by(3)
=> [[a, b, c], [d, e, f], [g, h, i]]
array.nest_by(4)
=> [[a, b, c, d], [e, f, g, h], [i, nil, nil, nil]]

The contents of the array can be anything (including array’s
themselves).
This is sort of like the rails Array.in_groups_of(n), but uses the
array.size to determine the number of elements in each new nested
array.

Any thoughts?

Daniel


#2

Ack, what I explained WAS array.in_groups_of(n).

What I’m ACTUALLY looking for is:
array.nest_by(2)
=> [[a, b, c, d, e], [f, g, h, i]]
array.nest_by(3)
=> [[a, b, c], [d, e, f], [g, h, i]]
array.nest_by(4)
=> [[a, b, c], [d, e], [f, g], [h, i]]

Where nest_by(n) produces n nested arrays. In the case of 2 and 4
when it’s not an balanced match, distribute the extras amongst first
arrays.


#3

On 7/13/07, danlunde removed_email_address@domain.invalid wrote:

arrays.
This is probably better posted in ruby-talk. Here’s a stab, though:

class Array
def nest_by(group_count)
in_groups_of(size / group_count + size %
group_count).collect(&:compact)
end
end

~ j.


#4

Thanks gene.tani, you got me close enough. The only problem with your
solution was that if there were 2 remaining elements, I wanted those
distributed among the first two nested array, not all in the first
array. Here’s the finished solution for what I was looking for:

class Array
def in_n_groups(n)
num_per_element, remainder = self.length.divmod(n);
orig = self.dup
temp = []
until orig.empty?
if remainder > 0
temp << orig.slice!(0, num_per_element+1)
remainder -= 1
else
temp << orig.slice!(0, num_per_element)
end
end
return temp
end
end

array = %w{ a b c d e f g h i j k }
array
=> [“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “j”, “k”]

array.in_n_groups(2)
=> [[“a”, “b”, “c”, “d”, “e”, “f”], [“g”, “h”, “i”, “j”, “k”]]

array.in_n_groups(3)
=> [[“a”, “b”, “c”, “d”], [“e”, “f”, “g”, “h”], [“i”, “j”, “k”]]

array.in_n_groups(4)
=> [[“a”, “b”, “c”], [“d”, “e”, “f”], [“g”, “h”, “i”], [“j”, “k”]]

Great for distributing long lists of data between balanced table
columns. Especially when you only want n columns.

Thanks everyone for helping.

Daniel


#5

On Jul 13, 2:59 pm, danlunde removed_email_address@domain.invalid wrote:

Ack, what I explained WAS array.in_groups_of(n).

What I’m ACTUALLY looking for is:
array.nest_by(2)
=> [[a, b, c, d, e], [f, g, h, i]]
array.nest_by(3)
=> [[a, b, c], [d, e, f], [g, h, i]]
array.nest_by(4)
=> [[a, b, c], [d, e], [f, g], [h, i]]

you could probably golf this, but:

require ‘enumerator’
class Array
def nest_by(length_of_out)
num_per_element, remainder =
self.length.divmod(length_of_out);
temp=[]
self.reverse.each_slice(num_per_element) { |slice| temp <<
slice}
if remainder>0
temp[-2]+= temp[-1]
temp.slice!(-1)
end
return temp.reverse
end
end