How to split an array in sub arrays of the same length

Hi,

Is there a way to split an array in sub arrays of the same length, that
is :

[ 1, 2, 3, 4, 5, 6, 7, 8 ].unknown_function(3)

that returns:

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

thanks in advance.

Hi,

Paolo Bacchilega <paolo.bacchilega@libero.it> writes:

Is there a way to split an array in sub arrays of the same length, that
is :

[ 1, 2, 3, 4, 5, 6, 7, 8 ].unknown_function(3)

that returns:

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

% irb --prompt simple

require 'enumerator'

=> true

[ 1, 2, 3, 4, 5, 6, 7, 8 ].enum_slice(3).to_a

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

···

--
eban

fr paulo:
# Is there a way to split an array in sub arrays of the same
# length, that
# is :

···

#
# [ 1, 2, 3, 4, 5, 6, 7, 8 ].unknown_function(3)
# that returns:
# [ [1, 2, 3], [4, 5, 6], [7, 8] ]

irb(main):021:0> x
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):022:0> y=[]
=> []
irb(main):023:0> x.each_slice(3){|s| y << s}
=> nil
irb(main):024:0> y
=> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

kind regards -botp

Although you could use Enumerator::each_slice as others have suggested, it might be simpler to augment Array with a partition function.

class Array

def partition(n, r=)
    raise ArgumentError if n <= 0
    if n <= size then
       r << first(n)
       last(size - n).partition(n, r)
    else
       r << self unless empty?
       return r
    end
end

end

p [ 1, 2, 3, 4, 5, 6, 7, 8 ].partition(1) => [[1], [2], [3], [4], [5], [6], [7], [8]]
p [ 1, 2, 3, 4, 5, 6, 7, 8 ].partition(3) => [[1, 2, 3], [4, 5, 6], [7, 8]]
p [ 1, 2, 3, 4, 5, 6, 7, 8 ].partition(4) => [[1, 2, 3, 4], [5, 6, 7, 8]]
p [ 1, 2, 3, 4, 5, 6, 7, 8 ].partition(6) => [[1, 2, 3, 4, 5, 6], [7, 8]]
p [ 1, 2, 3, 4, 5, 6, 7, 8 ].partition(8) => [[1, 2, 3, 4, 5, 6, 7, 8]]
p [ 1, 2, 3, 4, 5, 6, 7, 8 ].partition(10) => [[1, 2, 3, 4, 5, 6, 7, 8]]
p [ 1, 2, 3, 4, 5, 6, 7, 8 ].partition(-2) =>
      untitled text:6:in `partition': ArgumentError (ArgumentError)
       from untitled text:24

Hope this is helps.
Regards, Morton

···

On Jul 20, 2006, at 2:55 AM, Paolo Bacchilega wrote:

Hi,

Is there a way to split an array in sub arrays of the same length, that
is :

[ 1, 2, 3, 4, 5, 6, 7, 8 ].unknown_function(3)

that returns:

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

thanks in advance.

thanks to all for responding.

···

Il giorno gio, 20/07/2006 alle 06.53 +0000, Paolo Bacchilega ha scritto:

Hi,

Is there a way to split an array in sub arrays of the same length, that
is :

[ 1, 2, 3, 4, 5, 6, 7, 8 ].unknown_function(3)

that returns:

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

thanks in advance.

Paolo Bacchilega wrote:

Hi,

Is there a way to split an array in sub arrays of the same length, that
is :

[ 1, 2, 3, 4, 5, 6, 7, 8 ].unknown_function(3)

that returns:

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

thanks in advance.

Always remember that a program without inject is a
pointless program.

[ 1, 2, 3, 4, 5, 6, 7, 8 ].inject([]){|a,x|
  a.last.size<3 ? a.last << x : a << ; a }

Paolo Bacchilega wrote:

Is there a way to split an array in sub arrays of the same length, that

Stolen from the facets[1] extensions for Arrays[2]:

class Array
  def each_slice(n=nil, &yld)
    n = yld.arity.abs unless n
    i=0
    while i < self.length
      yld.call(*self.slice(i,n))
      i+=n
    end
  end
end

[1] http://facets.rubyforge.org/
[2] http://facets.rubyforge.org/api/core/classes/Array.html#M000163

Paolo Bacchilega wrote:

Is there a way to split an array in sub arrays of the same length, that
is :

[ 1, 2, 3, 4, 5, 6, 7, 8 ].unknown_function(3)

that returns:

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

Amuse yourself here:

http://redhanded.hobix.com/bits/matchingIntoMultipleAssignment.html

···

--
Posted via http://www.ruby-forum.com/\.

This is in enumerator, enumerator comes with ruby. Do we really need an "Optimized for Array" version? (That is its purpose according to the docs.)

Well surprising to me, there really is a noticeable speed difference.
I gues sit makes sense, each_slice in enumerator.c is written to use each, and not just call to_a first either. (Which makes sense, Files are Enumerables after all, maybe you want it in chunks of N lines at a time. You wouldn't want to have to slurp the whole file into memory just to do that).

I wonder if we can get this each_slice stuck in the standard lib for Array.

% cat enumerator_vs_facets.rb
#!/usr/bin/env ruby
require 'enumerator'
require 'benchmark'

class Array
   def facets_each_slice(n=nil, &yld)
     n = yld.arity.abs unless n
     i=0
     while i < self.length
       yld.call(*self.slice(i,n))
       i+=n
     end
   end
end

arrays = [ (1..97).to_a, (0..99).to_a, ["hello", "world"] ]
N = 1000
Benchmark.bmbm do |bm|
   bm.report("Facets: ") do
     N.times do
       arrays.each do |array|
         array.facets_each_slice(3) { |*x| "#{x}" }
       end
     end
   end

   bm.report("Enumerator: ") do
     N.times do
       arrays.each do |array|
         array.each_slice(3) { |*x| "#{x}" }
       end
     end
   end
end

% ruby enumerator_vs_facets.rb
Rehearsal ------------------------------------------------
Facets: 1.250000 0.010000 1.260000 ( 1.364671)
Enumerator: 1.380000 0.010000 1.390000 ( 1.442132)
--------------------------------------- total: 2.650000sec

                    user system total real
Facets: 1.250000 0.010000 1.260000 ( 1.315879)
Enumerator: 1.390000 0.010000 1.400000 ( 1.447592)

···

On Jul 20, 2006, at 4:40 PM, Phrogz wrote:

Paolo Bacchilega wrote:

Is there a way to split an array in sub arrays of the same length, that

Stolen from the facets[1] extensions for Arrays[2]:

class Array
  def each_slice(n=nil, &yld)
    n = yld.arity.abs unless n
    i=0
    while i < self.length
      yld.call(*self.slice(i,n))
      i+=n
    end
  end
end

[1] http://facets.rubyforge.org/
[2] http://facets.rubyforge.org/api/core/classes/Array.html#M000163

Logan Capaldo wrote:

% cat enumerator_vs_facets.rb
#!/usr/bin/env ruby
require 'enumerator'
require 'benchmark'

class Array
   def facets_each_slice(n=nil, &yld)
     n = yld.arity.abs unless n
     i=0
     while i < self.length
       yld.call(*self.slice(i,n))
       i+=n
     end
   end
end

arrays = [ (1..97).to_a, (0..99).to_a, ["hello", "world"] ]
N = 1000
Benchmark.bmbm do |bm|
   bm.report("Facets: ") do
     N.times do
       arrays.each do |array|
         array.facets_each_slice(3) { |*x| "#{x}" }
       end
     end
   end

   bm.report("Enumerator: ") do
     N.times do
       arrays.each do |array|
         array.each_slice(3) { |*x| "#{x}" }
       end
     end
   end
end

% ruby enumerator_vs_facets.rb
Rehearsal ------------------------------------------------
Facets: 1.250000 0.010000 1.260000 ( 1.364671)
Enumerator: 1.380000 0.010000 1.390000 ( 1.442132)
--------------------------------------- total: 2.650000sec

                    user system total real
Facets: 1.250000 0.010000 1.260000 ( 1.315879)
Enumerator: 1.390000 0.010000 1.400000 ( 1.447592)

I believe the OP asked how to split an array in "x" sub arrays of equal
length.
To me, it appears that the response show how to partition an array into
sub arrays of "y" elements.

The following method does what the OP asked for, as far as I read it.

class Array
  def split(n, b=, d=)

···

#########################################################################
    # evenly split array 'a' into 'n' sub arrays
    #########################################################################
    if self.size == 0 or n <= 0
      b = nil
    elsif n == 1
      b = *self
    else
      # determine how many elements of the array should go in each sub
arrays
      buckets = n
      length = self.size
      while buckets > 0
        elements = length / buckets + (length % buckets > 0 ? 1 : 0)
        d << elements
        length -= elements
        buckets -= 1
      end
      # p d

      # evenly distribute array elements into an array with 'n' sub arrays
      start = 0 # start
      0.upto(n-1) do |idx|
        len = d[idx]
        b[idx] = *self.slice(start,len)
        start += len
      end
    end
    b
  end
end

bbiker wrote:

I believe the OP asked how to split an array in "x" sub arrays of equal
length.
To me, it appears that the response show how to partition an array into
sub arrays of "y" elements.

The following method does what the OP asked for, as far as I read it.

class Array
  def split(n, b=, d=)
    #########################################################################
    # evenly split array 'a' into 'n' sub arrays
    #########################################################################
    if self.size == 0 or n <= 0
      b = nil
    elsif n == 1
      b = *self
    else
      # determine how many elements of the array should go in each sub
arrays
      buckets = n
      length = self.size
      while buckets > 0
        elements = length / buckets + (length % buckets > 0 ? 1 : 0)
        d << elements
        length -= elements
        buckets -= 1
      end
      # p d

      # evenly distribute array elements into an array with 'n' sub arrays
      start = 0 # start
      0.upto(n-1) do |idx|
        len = d[idx]
        b[idx] = *self.slice(start,len)
        start += len
      end
    end
    b
  end
end

Sorry, but I pressed the Post message button too soon.

c =
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23].split(5)
=>

[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18,
19], [20, 21, 22, 23]]

Note that the first 3 sub arrays contain 5 elements and the last 2 sub
arrays contain 4 elements.

I am a Ruby newbie and would appreciate a critique on the method I
posted.

Thank You

bbiker wrote:

bbiker wrote:
> I believe the OP asked how to split an array in "x" sub arrays of equal
> length.
> To me, it appears that the response show how to partition an array into
> sub arrays of "y" elements.
>
> The following method does what the OP asked for, as far as I read it.
>
> class Array
> def split(n, b=, d=)
> #########################################################################
> # evenly split array 'a' into 'n' sub arrays
> #########################################################################
> if self.size == 0 or n <= 0
> b = nil
> elsif n == 1
> b = *self
> else
> # determine how many elements of the array should go in each sub
> arrays
> buckets = n
> length = self.size
> while buckets > 0
> elements = length / buckets + (length % buckets > 0 ? 1 : 0)
> d << elements
> length -= elements
> buckets -= 1
> end
> # p d
>
> # evenly distribute array elements into an array with 'n' sub arrays
> start = 0 # start
> 0.upto(n-1) do |idx|
> len = d[idx]
> b[idx] = *self.slice(start,len)
> start += len
> end
> end
> b
> end
> end

Sorry, but I pressed the Post message button too soon.

c =
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23].split(5)
=>

[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18,
19], [20, 21, 22, 23]]

Note that the first 3 sub arrays contain 5 elements and the last 2 sub
arrays contain 4 elements.

I am a Ruby newbie and would appreciate a critique on the method I
posted.

Thank You

class Array
  def split n
    count , fat_ones = self.size / n , self.size % n
    self.inject( [] ){ |a,e|
      a.last.size < count + ( a.size <= fat_ones ? 1 : 0 ) ?
        a.last << e : a << [e] ; a }
  end
end

a = (1..23).to_a
p a.split(5)
p a

William James wrote:

bbiker wrote:
> bbiker wrote:
> > I believe the OP asked how to split an array in "x" sub arrays of equal
> > length.
> > To me, it appears that the response show how to partition an array into
> > sub arrays of "y" elements.
> >
> > The following method does what the OP asked for, as far as I read it.
> >
> > class Array
> > def split(n, b=, d=)
> > #########################################################################
> > # evenly split array 'a' into 'n' sub arrays
> > #########################################################################
> > if self.size == 0 or n <= 0
> > b = nil
> > elsif n == 1
> > b = *self
> > else
> > # determine how many elements of the array should go in each sub
> > arrays
> > buckets = n
> > length = self.size
> > while buckets > 0
> > elements = length / buckets + (length % buckets > 0 ? 1 : 0)
> > d << elements
> > length -= elements
> > buckets -= 1
> > end
> > # p d
> >
> > # evenly distribute array elements into an array with 'n' sub arrays
> > start = 0 # start
> > 0.upto(n-1) do |idx|
> > len = d[idx]
> > b[idx] = *self.slice(start,len)
> > start += len
> > end
> > end
> > b
> > end
> > end
>
> Sorry, but I pressed the Post message button too soon.
>
> c =
> [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23].split(5)
> =>
>
> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18,
> 19], [20, 21, 22, 23]]
>
> Note that the first 3 sub arrays contain 5 elements and the last 2 sub
> arrays contain 4 elements.
>
> I am a Ruby newbie and would appreciate a critique on the method I
> posted.
>
> Thank You

class Array
  def split n
    count , fat_ones = self.size / n , self.size % n
    self.inject( [] ){ |a,e|
      a.last.size < count + ( a.size <= fat_ones ? 1 : 0 ) ?
        a.last << e : a << [e] ; a }
  end
end

a = (1..23).to_a
p a.split(5)
p a

Perhaps faster:

class Array
  def split n
    count , fat_ones = self.size / n , self.size % n
    cp, out = self.dup,
    out << cp.
      slice!(0, count + (out.size < fat_ones ? 1 : 0 )) while cp!=
    out
  end
end

William James wrote:

> I am a Ruby newbie and would appreciate a critique on the method I
> posted.
>
> Thank You

class Array
  def split n
    count , fat_ones = self.size / n , self.size % n
    self.inject( [] ){ |a,e|
      a.last.size < count + ( a.size <= fat_ones ? 1 : 0 ) ?
        a.last << e : a << [e] ; a }
  end
end

a = (1..23).to_a
p a.split(5)
p a

Yes! it is a much better way !!!
Since obviously I am not familiar with the inject method, I took to the
pick axe and irb to work it out until I understood exactly what you
were doing.

The only question left is how to best handle bad inputs?

n <= 0, n not specified, and an empty array, n > [1,2,3,4,5].size

for [1,2,3,4,5].split(0) => Exception: divided by 0

for [1,2,3,4,5].split(-n) => [, [1], [2], [3], [4], [5]]

for .split(+/-n) => [] where n != 0
for .split(0) => Exception: divided by 0

for [1,2,3,4,5].split() =>
     Exception: wrong number of arguments (0 for 1)

for [1,2,3,4,5].split(8) => [[1], [2], [3], [4], [5]]

I would guess this might be the correct way. This could cause a problem
if the user were to check the size of the returned array For the above
example it would be5 instead of the expected 8.

You could say that for n == 0 or n not specifiend are already handled
by existing Exceptions. Do need to handle negative values of n, n >
array.size and array.size == 0