DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Split Array Into Smaller Arrays Of Equal Size

02.09.2007
| 30019 views |
  • submit to reddit
        Split an array of elements into a set of smaller arrays of equal size. Extra elements are preferentially assigned to earlier arrays. If there are no elements in a given returned array it will be [] (empty array)

# use as standalone function
def chunk_array(array, pieces=2)
  len = array.length;
  mid = (len/pieces)
  chunks = []
  start = 0
  1.upto(pieces) do |i|
    last = start+mid
    last = last-1 unless len%pieces >= i
    chunks << array[start..last] || []
    start = last+1
  end
  chunks
end

# use as array.chunk
class Array
  def chunk(pieces=2)
    len = self.length;
    mid = (len/pieces)
    chunks = []
    start = 0
    1.upto(pieces) do |i|
      last = start+mid
      last = last-1 unless len%pieces >= i
      chunks << self[start..last] || []
      start = last+1
    end
    chunks
  end
end


Examples of use:

>> chunk_array [1,2,3,4,5,6], 2
=> [[1, 2, 3], [4, 5, 6]]

>> chunk_array [1,2,3,4,5,6], 3
=> [[1, 2], [3, 4], [5, 6]]

>> chunk_array [1,2,3,4,5,6], 4
=> [[1, 2], [3, 4], [5], [6]]

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

>> chunk_array [1,2,3], 4
=> [[1], [2], [3], []]

>> chunk_array [], 2
=> [[], []]

if you prefer the second form (more ruby-ish, but not always appropriate)

>> [1,2,3,4,5,6,7,8,9,10].chunk
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]

>> [1,2,3,4,5,6,7,8,9,10].chunk 3
=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]

This is handy when used with a splat because you can do things like:

left, right = *chunk_array(all,2)
    

Comments

Snippets Manager replied on Wed, 2011/07/27 - 8:36am

Since each_slice returns an enumerator, this might be a bit more consistent/elegant/straightforward: a.each_slice(n).to_a

Alexey Tarasevich replied on Wed, 2010/01/06 - 7:52pm

and variation: a.each_slice(n).map{|x|[x]}.reduce([], &:+)

Alexey Tarasevich replied on Wed, 2010/01/06 - 7:52pm

here is my one-liner: def chunk n each_slice(n).reduce([]) {|x,y| x += [y] } end

Snippets Manager replied on Mon, 2011/04/04 - 2:18pm

Why so complicated? a.each_slice(n).map

Snippets Manager replied on Fri, 2006/06/02 - 8:45pm

Here's my translation to PHP (using the #chunk_array from the original post): function chunk_array($array, $pieces=2) { $len = count($array); $mid = ($len/$pieces); $chunks = array(); $start = 0; foreach (range(1, $pieces) as $i) { $last = $start + $mid; if ($len % $pieces < i) $last = $last-1; $chunk_length = $last - $start; if ($chunk_length < 0) $chunk_length = 0; $chunks[] = array_slice($array, $start, $chunk_length); $start = $last+1; } return $chunks; }

Snippets Manager replied on Tue, 2007/12/04 - 2:28am

Unfortunately ruby can suffer with deep recursion--though maybe it has tail-call optimization, do you happen to know? Anyhow, here's my pretty short solution: require 'enumerator' class Array def chunk(pieces) q, r = length.divmod(pieces) (0..pieces).map { |i| i * q + [r, i].min }.enum_cons(2) \ .map { |a, b| slice(a...b) } end end

Snippets Manager replied on Tue, 2007/06/05 - 3:03pm

An even shorter version, using recursion: class Array def chunk(pieces) return [] if pieces.zero? piece_size = (length.to_f / pieces).ceil [first(piece_size), *last(length - piece_size).chunk(pieces - 1)] end end

Joe McGlynn replied on Thu, 2007/02/15 - 3:43am

A shorter format, from "Why's Poignant guide to ruby": class Array def / len a = [] each_with_index do |x,i| a << [] if i % len == 0 a.last << x end a end end [:now, :is, :the, :time, :for, :all, :good, :men, :to, :come, :to, :the, :aid] / 3 gives => [[:now, :is, :the], [:time, :for, :all], [:good, :men, :to], [:come, :to, :the], [:aid]]