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

Discussion in 'Ruby' started by Paolo Bacchilega, Jul 20, 2006.

  1. 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, Jul 20, 2006
    #1
    1. Advertising

  2. Hi,

    Paolo Bacchilega <> 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
     
    WATANABE Hirofumi, Jul 20, 2006
    #2
    1. Advertising

  3. Paolo Bacchilega

    Peña, Botp Guest

    fr paulo:
    # Is there a way to split an array in sub arrays of the same=20
    # length, that
    # is :
    #=20
    # [ 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
    =3D> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    irb(main):022:0> y=3D[]
    =3D> []
    irb(main):023:0> x.each_slice(3){|s| y << s}
    =3D> nil
    irb(main):024:0> y
    =3D> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

    kind regards -botp
     
    Peña, Botp, Jul 20, 2006
    #3
  4. 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.
     
    Morton Goldberg, Jul 20, 2006
    #4
  5. 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.


    thanks to all for responding.
     
    Paolo Bacchilega, Jul 20, 2006
    #5
  6. 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 << [x]; a }
     
    William James, Jul 20, 2006
    #6
  7. Paolo Bacchilega

    Phrogz Guest

    Phrogz, Jul 20, 2006
    #7
  8. 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
    >
    >


    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)
     
    Logan Capaldo, Jul 21, 2006
    #8
  9. Paolo Bacchilega

    bbiker Guest

    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, Jul 29, 2006
    #9
  10. Paolo Bacchilega

    bbiker Guest

    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, Jul 29, 2006
    #10
  11. 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, Jul 29, 2006
    #11
  12. 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, Jul 29, 2006
    #12
  13. Paolo Bacchilega

    Dr Nic Guest

    Dr Nic, Jul 29, 2006
    #13
  14. Paolo Bacchilega

    bbiker Guest

    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
     
    bbiker, Jul 29, 2006
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ben
    Replies:
    2
    Views:
    955
  2. Philipp
    Replies:
    21
    Views:
    1,186
    Philipp
    Jan 20, 2009
  3. Lawrence D'Oliveiro

    Death To Sub-Sub-Sub-Directories!

    Lawrence D'Oliveiro, May 5, 2011, in forum: Java
    Replies:
    92
    Views:
    2,169
    Lawrence D'Oliveiro
    May 20, 2011
  4. Tom
    Replies:
    3
    Views:
    234
    salsablr
    Dec 20, 2004
  5. Tuan  Bui
    Replies:
    14
    Views:
    526
    it_says_BALLS_on_your forehead
    Jul 29, 2005
Loading...

Share This Page