Splitting an array into "even" parts

Discussion in 'Perl Misc' started by Tore Aursand, Nov 5, 2003.

  1. Tore Aursand

    Tore Aursand Guest

    Hi there!

    I need to split an array into "even" parts, so that each resulting array
    contains the same number of elements.

    Think of it as splitting a list of many names into multiple columns, and
    you want x numbers of names in each column so that each column gets filled
    up even-wise.

    The problem arise, of course, when you have 10 elements and want to split
    those into 3 columns. That should be listed like this:

    +-------+-------+-------+
    | Col 1 | Col 2 | Col 3 |
    +-------+-------+-------+
    | 1 | 5 | 9 |
    | 2 | 6 | 10 |
    | 3 | 7 | |
    | 4 | 8 | |
    +-------+-------+-------+

    So far I've come up with this solution:

    sub split_array {
    my $array = shift; # Array reference
    my $parts = shift; # Number of columns to split into

    ## Make sure $parts is set (default = 2), and that it isn't
    ## larger than the number of elements in $array
    $parts ||= 2;
    $parts = @$array if ( $parts > @$array );

    ## Split
    my @split = (); # Will contain the splitted data
    my $slice = POSIX::ceil( @$array / $parts ); # Size of each part

    for ( 1 .. $parts ) {
    if ( $_ == $parts ) {
    push( @split, $array );
    }
    else {
    push( @split, splice(@$array, 0, $slice) );
    }
    }

    ## Return
    return \@split;
    }

    This code is doubtly the most efficient (I'm always looking for
    efficient, yet readable, ways to do things), and I haven't run that many
    tests on it either. Any comments on it?


    --
    Tore Aursand <>
    Tore Aursand, Nov 5, 2003
    #1
    1. Advertising

  2. In article <>, Tore Aursand wrote:
    > Hi there!
    >
    > I need to split an array into "even" parts, so that each resulting array
    > contains the same number of elements.

    [cut]
    > This code is doubtly the most efficient (I'm always looking for
    > efficient, yet readable, ways to do things), and I haven't run that many
    > tests on it either. Any comments on it?


    Here's something that doesn't rely on external modules:

    sub split_array
    {
    my $arr = shift;
    my $bins = shift;

    my $size = int((scalar @{ $arr })/$bins);

    if (scalar @{ $arr } % $bins != 0) {
    ++$size;
    }

    my @result;

    for my $i (0 .. ($bins - 1)) {
    push @result, [ @{ $arr }[$i*$size .. (($i + 1)*$size - 1)] ];
    }

    return @result;
    }

    --
    Andreas Kähäri
    Andreas Kahari, Nov 5, 2003
    #2
    1. Advertising

  3. -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Tore Aursand <> wrote in
    news:p:

    > Hi there!
    >
    > I need to split an array into "even" parts, so that each resulting
    > array contains the same number of elements.
    >
    > Think of it as splitting a list of many names into multiple columns,
    > and you want x numbers of names in each column so that each column
    > gets filled up even-wise.
    >
    > The problem arise, of course, when you have 10 elements and want to
    > split those into 3 columns. That should be listed like this:
    >
    > +-------+-------+-------+
    > | Col 1 | Col 2 | Col 3 |
    > +-------+-------+-------+
    > | 1 | 5 | 9 |
    > | 2 | 6 | 10 |
    > | 3 | 7 | |
    > | 4 | 8 | |
    > +-------+-------+-------+
    >
    > So far I've come up with this solution:
    >
    > sub split_array {
    > my $array = shift; # Array reference
    > my $parts = shift; # Number of columns to split into
    >
    > ## Make sure $parts is set (default = 2), and that it isn't
    > ## larger than the number of elements in $array
    > $parts ||= 2;
    > $parts = @$array if ( $parts > @$array );
    >
    > ## Split
    > my @split = (); # Will contain the splitted data
    > my $slice = POSIX::ceil( @$array / $parts ); # Size of each part
    >
    > for ( 1 .. $parts ) {
    > if ( $_ == $parts ) {
    > push( @split, $array );
    > }
    > else {
    > push( @split, splice(@$array, 0, $slice) );
    > }
    > }
    >
    > ## Return
    > return \@split;
    > }
    >
    > This code is doubtly the most efficient (I'm always looking for
    > efficient, yet readable, ways to do things), and I haven't run that
    > many tests on it either. Any comments on it?


    aplice() pretty much does what you want by itself -- there's no need for
    all the special-case-handling code you are surrounding it with.

    sub split_array
    {
    my $array = shift;
    my $parts = shift || 2;
    my @array = @$array; # working copy
    my @split; # result

    while (@array)
    {
    push @split, [splice @array, 0, $parts];
    }
    return \@split;
    }

    or even:

    push @split, [splice @array, 0, $parts] while @array;

    - --
    Eric
    $_ = reverse sort $ /. r , qw p ekca lre uJ reh
    ts p , map $ _. $ " , qw e p h tona e and print

    -----BEGIN PGP SIGNATURE-----
    Version: PGPfreeware 7.0.3 for non-commercial use <http://www.pgp.com>

    iQA/AwUBP6jsRmPeouIeTNHoEQJN/gCdG6n983iPY7jp14peWtV09TyG21AAnRH1
    pu+8ZH/a6uRkk+sUbRLGbfx0
    =fmD3
    -----END PGP SIGNATURE-----
    Eric J. Roode, Nov 5, 2003
    #3
  4. Tore Aursand

    Tore Aursand Guest

    On Wed, 05 Nov 2003 06:25:41 -0600, Eric J. Roode wrote:
    > sub split_array
    > {
    > my $array = shift;
    > my $parts = shift || 2;
    > my @array = @$array; # working copy
    > my @split; # result
    >
    > while (@array)
    > {
    > push @split, [splice @array, 0, $parts];
    > }
    > return \@split;
    > }


    Fine solution, indeed, but still not satisfying me needs. Let's take a
    look at an example:

    my @array = ( 1..9 );
    my $parts = 4;

    Expected result should be:

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

    But your function only returns 3 arrays. A note begins to form in my back
    head: Make sure that @array is splittable in $parts parts. :)


    --
    Tore Aursand <>
    Tore Aursand, Nov 5, 2003
    #4
  5. Tore Aursand

    Anno Siegel Guest

    Tore Aursand <> wrote in comp.lang.perl.misc:
    > On Wed, 05 Nov 2003 06:25:41 -0600, Eric J. Roode wrote:
    > > sub split_array
    > > {
    > > my $array = shift;
    > > my $parts = shift || 2;
    > > my @array = @$array; # working copy
    > > my @split; # result
    > >
    > > while (@array)
    > > {
    > > push @split, [splice @array, 0, $parts];
    > > }
    > > return \@split;
    > > }

    >
    > Fine solution, indeed, but still not satisfying me needs. Let's take a
    > look at an example:
    >
    > my @array = ( 1..9 );
    > my $parts = 4;
    >
    > Expected result should be:
    >
    > [
    > [1,2,3],
    > [4,5],
    > [6,7],
    > [8,9]
    > ]


    Well, here's a variant that does that. It returns the array of split
    parts directly, not a reference to it, but that's trivial to fix.

    sub split_array {
    my $parts = shift || 2;
    my @array = @{ shift()};
    my $size = int @array/$parts;
    my $rem = @array % $parts;
    map [splice @array, 0, $size + ($rem-- > 0)], 1 .. $parts;
    }


    Anno
    Anno Siegel, Nov 5, 2003
    #5
  6. Tore Aursand

    Tore Aursand Guest

    On Wed, 05 Nov 2003 13:52:42 +0000, Anno Siegel wrote:
    > sub split_array {
    > my $parts = shift || 2;
    > my @array = @{ shift()};
    > my $size = int @array/$parts;
    > my $rem = @array % $parts;
    > map [splice @array, 0, $size + ($rem-- > 0)], 1 .. $parts;
    > }


    Most excellent. Seems to do excactly what I am looking for. Thanks a
    lot, Anno!


    --
    Tore Aursand <>
    Tore Aursand, Nov 5, 2003
    #6
    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. Andy Fish
    Replies:
    4
    Views:
    368
    Jon A. Cruz
    Aug 24, 2003
  2. Fabian Steiner

    Splitting device addresses into parts

    Fabian Steiner, Sep 26, 2006, in forum: Python
    Replies:
    9
    Views:
    265
    Lawrence D'Oliveiro
    Sep 27, 2006
  3. AG
    Replies:
    5
    Views:
    439
    =?iso-8859-1?q?Erik_Wikstr=F6m?=
    Jan 18, 2007
  4. Norah Jones
    Replies:
    0
    Views:
    117
    Norah Jones
    Mar 27, 2013
  5. Peter Otten
    Replies:
    4
    Views:
    171
    Chris Angelico
    Mar 28, 2013
Loading...

Share This Page