Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Syntactically cool list of lists

by jettero (Monsignor)
on Dec 20, 2006 at 21:08 UTC ( [id://590988]=perlquestion: print w/replies, xml ) Need Help??

jettero has asked for the wisdom of the Perl Monks concerning the following question:

You guys, I'm looking for the syntactically coolest looking way to build a list of lists from a list.

@a = (1,5,7,9,32,197,8,4,5); # (pretend this is dynamically generate +d) @b = map {[ $a[2*$_], $a[2*$_+1]||() ]} 0 .. int $#a/2;

Above is the best I'd come up with so far for generating @b, but I'm not sure it's the syntactically coolest possible way to do it. I'd also like to generalize it to a list of lists of size $l rather than always being $l=2.

This is a highly subjective problem and I'm already about responses.

-Paul

Replies are listed 'Best First'.
Re: Syntactically cool list of lists
by GrandFather (Saint) on Dec 20, 2006 at 21:14 UTC
    push @b, [splice @a, 0, $l] while @a;

    is fairly clean. Check the result with:

    print "@$_\n" for @b;

    DWIM is Perl's answer to Gödel

      Clean but destructive. So, make a copy of @a if you need to keep it.

      -sauoq
      "My two cents aren't worth a dime.";
      That is a really cool way to do it. I always forget about splice(). I wish it didn't destroy @a, but it's definitely more readable than the map mess I figured out last night...

      -Paul

Re: Syntactically cool list of lists
by BrowserUk (Patriarch) on Dec 20, 2006 at 22:29 UTC

    I dislike the splice solution because it destroys the source array unless you copy it first. I have a utility routine called mapn which lends itself to this task amongst others:

    use Data::Dumper; $data::Dumper::Terse=1; $Data::Dumper::Indent=0; sub mapn (&@) { my( $c, $n, $s ) = ( shift, shift, 0 ); map{ $s += $n; $c->( @_[ ( $s - $n ) .. ( $s < @_ ? $s - 1 : @_ - 1 ) ] ); } 0 .. ( $#_ / $n ); } my @a = (1,5,7,9,32,197,8,4,5); for my $n ( 2 .. 7 ) { my $LoL = [ mapn{ [ @_ ] } $n, @a ]; print "n=$n ", Dumper $LoL; } __END__ n=2 $VAR1 = [[1,5],[7,9],[32,197],[8,4],[5]]; n=3 $VAR1 = [[1,5,7],[9,32,197],[8,4,5]]; n=4 $VAR1 = [[1,5,7,9],[32,197,8,4],[5]]; n=5 $VAR1 = [[1,5,7,9,32],[197,8,4,5]]; n=6 $VAR1 = [[1,5,7,9,32,197],[8,4,5]]; n=7 $VAR1 = [[1,5,7,9,32,197,8],[4,5]];

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Syntactically cool list of lists
by Fletch (Bishop) on Dec 20, 2006 at 21:14 UTC

    That's not cool, that's just ugly trying to masquerade as clever.

    my @a = ( [ 1, 5 ], [ 7. 9 ], [ 32, 197 ], [ 8, 4 ], [ 5 ], );

    It's immediately obvious what @a contains. It's (fairly) readily maintained. Another alternative would be to store it as YAML and use YAML (or YAML::Syck) to Load it from a heredoc.

    Update: Now seeing GrandFather's response I get the impression that you're asking about a general case of converting a list to an nx2 LoL and not initializing something from a static array? If that's the case, I like his reply. If you're talking about going from a static list of data, I still say go with the YAML.

Re: Syntactically cool list of lists
by rhesa (Vicar) on Dec 20, 2006 at 21:41 UTC
    I thought about using the natatime function from List::MoreUtils. It returns an iterator however, which is a bit clumsy in this context, so I looked at the source for natatime. I always forget about splice, but there it was :-)

    FWIW, this is what I whipped up:

    #!/usr/bin/perl -l use strict; sub partn($@) { my $n = shift; my @l = @_; my @r; push @r, [splice( @l, 0, $n )] while @l; return @r; } ## examples my @r = (1,5,7,9,32,197,8,4,5); my @r2 = partn 2, @r; my @r3 = partn 3, @r; my @r4 = partn 4, @r; use Data::Dumper; $Data::Dumper::Indent = 0; print Dumper \@r2; print Dumper \@r3; print Dumper \@r4; __END__ $VAR1 = [[1,5],[7,9],[32,197],[8,4],[5]]; $VAR1 = [[1,5,7],[9,32,197],[8,4,5]]; $VAR1 = [[1,5,7,9],[32,197,8,4],[5]];
      Doesn't seem to be that clumsy.
      use List::MoreUtils qw( natatime ); my $iter = natatime $l, @a; while (my @row = $iter->()) { push @b, \@row; }

      Update: Switched to using a while loop, since the while statement modifier doesn't work here.

        I was hoping someone would post something that iterates.

        -Paul

Re: Syntactically cool list of lists
by rir (Vicar) on Dec 21, 2006 at 01:32 UTC
    chunk( $subarray_length, \@array_to_partition ); sub chunk { my ($l, $in) = @_; my $arr = 0; my $ret; do { print( "die (Error Domain$/)") , return if $l < 1; # XXX @{ $ret->[$arr] } = @$in[ $arr * $l .. ( $arr * $l + $l - 1 >= @$in ? $#$in : $arr * $l + $l - +1 ) ]; ++$arr; } until ( $arr * $l > @$in - 1 ); return $ret; }
    Be well,
    rir
Re: Syntactically cool list of lists
by sgifford (Prior) on Dec 21, 2006 at 04:05 UTC
    Here are two that I like. The first fills unused subarray elements with undef.
    sub groups_of_n { my $n = shift; return map { [@_[$_*$n..$_*$n+$n-1]] } 0..$#_/$n; } use POSIX qw(ceil); sub groups_of_n { my $n = shift; my $m = ceil(@_/$n); my @r; my $i = 0; push(@{$r[$i++%$m]},$_) foreach @_; @r; }
      The first fills unused subarray elements with undef.

      This gets rid of the undefs.

      sub groups_of_n { my $n = shift; return map { [ grep {defined} @$_ ] } map { [ @_ [$_ * $n .. $_ * $n + $n -1 ]] } 0 .. $#_ / $n; }

      Cheers,

      JohnGG

        True, or we can avoid generating the elements in the first place:
        sub groups_of_n { sub min { return $_[0] < $_[1] ? $_[0] : $_[1]; } my $n = shift; return map { [ @_[$_*$n..min($_*$n+$n-1,$#_)]] } 0..$#_/$n; }
Re: Syntactically cool list of lists
by shmem (Chancellor) on Dec 21, 2006 at 11:00 UTC

    At the cost of a temporary counter variable

    my $c = -$l; push @b, [ grep {defined $_} @a[($c+=$l)..($c+$l-1)] ] for 0 .. $#a/$l

    if you don't mind extending @a to a multiple of $l ;-)

    <update>
    Oops - almost identical to JohnGG's solution above - and his uses $_ as counter. hrm :-/

    push @b, [ grep {defined $_} @a[$_*$l .. ($_*$l+$l>$#a ? $#a : $_*$l+$ +l-1)] ] for 0 .. $#a/$l;

    This one doesn't extend @a. But readable, well... it's the best I can contrieve in a single line (up to ";" that is).

    </update>

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      I can't claim it was my solution, just a refinement of sgifford's :)

      Cheers,

      JohnGG

Re: Syntactically cool list of lists
by tsee (Curate) on Dec 22, 2006 at 11:46 UTC

    Somehow, my contribution to this was lost. I quietly suspect the fault is sitting in front of the keyboard and it's all related to the preview-button.

    Hence I write this again. This solution has the added benefit of trivially supporting partitions other than two per sublist. Autovivification++!

    my @a = (1..21); my @b; push @{$b[$_/2]}, $a[$_] for 0..$#a;

    Cheers,
    Steffen

Re: Syntactically cool list of lists
by druud (Sexton) on Dec 22, 2006 at 17:07 UTC
    $ perl -MData::Dumper -wle ' $s = 5; @a = (1,5,7,9,32,197,8,4,5); $e=int $#a/$s; @b = map {$_*=$s; [@a[$_..($_+$s>$#a?$#a:$_+$s-1)]]} 0..$e; print Dumper \@b '
    $VAR1 = [ [ 1, 5, 7, 9, 32 ], [ 197, 8, 4, 5 ] ];

    -- 
    Ruud

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://590988]
Approved by GrandFather
Front-paged by andyford
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (7)
As of 2024-04-26 08:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found