Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby "bucketize" an array

Suppose I have the following array:

a = (1..10).to_a

Is there a single in-built ruby (or rails) function that is capable or splitting the array into exactly N roughly equal parts while maintaining the order?

I'm looking for something like this:

a.bucketize(3)
=> [[1,2,3,4],[5,6,7],[8,9,10]]
a.bucketize(5)
=> [[1,2],[3,4],[5,6],[7,8],[9,10]]

Hint: each_slice doesn't do this.

Also, I know I could write this function myself and open up the Array class or Enumerable module.

Thanks.

like image 721
Finbarr Avatar asked Oct 17 '25 10:10

Finbarr


2 Answers

I'd do it like this:

ary = (1..10).to_a

ary.each_slice((ary.length.to_f/3).ceil).to_a
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]

ary.each_slice((ary.length.to_f/5).ceil).to_a
=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

It's not perfect, but it does come close:

ary = (1..9).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

ary.each_slice((ary.length.to_f/2).ceil).to_a
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9]]

ary.each_slice((ary.length.to_f/3).ceil).to_a
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

ary.each_slice((ary.length.to_f/4).ceil).to_a
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
like image 188
the Tin Man Avatar answered Oct 19 '25 02:10

the Tin Man


This kind of task is best tackled using a functional approach. Here's a tail-recursive functional implementation (except for the unavoidable << to accumulate efficiently on arrays):

class Array
  def bucketize(n, index = 0, acc = [])
    return acc if n <= 0 || size <= index
    n0 = ((size - index).to_f / n).ceil
    bucketize(n - 1, index + n0, acc << self[index, n0])
  end
end

(1..9).to_a.bucketize(3)
#=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
(1..10).to_a.bucketize(3)
#=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
(1..11).to_a.bucketize(3)
#=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]
like image 27
tokland Avatar answered Oct 19 '25 01:10

tokland