MelonSmasher
MelonSmasher

Reputation: 670

Ruby split array into X groups

I need to split an array into X smaller array. I don't care about the number of elements in the smaller arrays I just need to create X arrays from a larger one. I've been doing some reading and it seems like I need a method similar to the in_groups method from rails. I am not using rails right now, just ruby.

Upvotes: 4

Views: 5518

Answers (5)

if x is a count of groups:

x = 2
a = [1,2,3,4,5,6,7,8,9,10,11,12]
a.in_groups(x)

=> [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]

if group by x pieces:

x = 2
a = [1,2,3,4,5,6,7,8,9,10,11,12]
a.each_slice(x).to_a

=> [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]

Upvotes: 3

lobati
lobati

Reputation: 10215

If you need to have N groups, you can use the in_groups monkey-patch provided by ActiveSupport, as mentioned in another answer:

require 'active_support/core_ext/array/grouping'
my_array = [1,2,3,4,5]
my_array.in_groups(2)
# => [[1, 2, 3], [4, 5, nil]]
my_array.in_groups(2, false)
# => [[1, 2, 3], [4, 5]]

If you care about the number of elements in the group as opposed to the number of groups, you can use each_slice provided by Ruby core:

my_array = [1,2,3,4,5]
my_array.each_slice(2).to_a
# => [[1, 2], [3, 4], [5]]

Upvotes: 0

MelonSmasher
MelonSmasher

Reputation: 670

I decided to put:

require 'active_support/core_ext/array/grouping'

Upvotes: 2

David Hempy
David Hempy

Reputation: 6237

Requiring Rails just to get that function is overkill. Just use each_slice:

team = ['alice', 'andy', 'bob', 'barry', 'chloe', 'charlie']
=> ["alice", "andy", "bob", "barry", "chloe", "charlie"]

team.each_slice(2).to_a
=> [["alice", "andy"], ["bob", "barry"], ["chloe", "charlie"]]

each_slice's parameter is the number of elements in each slice (except possibly the last slice). Since you're looking for X slices, you can do something like this:

team.each_slice(team.length/X).to_a

That's not perfect, as you'll get one extra slice if the array length is not a multiple of X, but gets you in the ballpark and you can tweak it from there depending on what exactly your needs are.

Since you say you don't care how many are in each, you could just use the length/x approach above, then check to see if you have one too many slices. If so, just join the last two slices into one jumbo-size slice. This might avoid some fussy math or floating point operations.

Upvotes: 7

Yu Hao
Yu Hao

Reputation: 122383

You can make your own method, here's a basic idea:

class Array
  def my_group(x)
    start = 0
    size = (self.size() / Float(x)).ceil
    while x > 0
      yield self[start, size]
      size = ((self.size() - 1 - start) / Float(x)).ceil
      start += size
      x -= 1
    end
  end
end

%w(1 2 3 4 5 6 7 8 9 10).my_group(3) {|group| p group}
# =>["1", "2", "3", "4"]
# =>["4", "5", "6"]
# =>["7", "8", "9"]

Upvotes: 2

Related Questions