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

let's say I have an array: $A=[ 1..100 ]; and I want to be able to reform this list into a LoL where each element is N-long. Ie, $A{3} = [ [1,2,3], [4,5,6] ]; but it should be parametric, thus I can also easily create $A{4}= [ [1,2,3,4], [5,6,7,8] ]

Replies are listed 'Best First'.
Re: list to LoL
by lhoward (Vicar) on Jun 02, 2000 at 21:28 UTC
    Here's my solution.... There's some unclean code in there to make it handle fencepost conditions that I could probably optimize out, but haven't yet. Would also be faster if I passed array references in and out of the reform_list subroutine.
    use Data::Dumper; my @a=(1..100); my @a3=reform_list(3,@a); print Dumper(@a3); my @a4=reform_list(4,@a); print Dumper(@a4); sub reform_list{ my $size=shift; my @list=@_; my @nlist=(); my $max=int(scalar(@list)/$size); for(my $c=0;$c<=$max;$c++){ my $a=$c*$size; my $b=($c+1)*$size-1; if($b>=scalar(@list)){ $b=scalar(@list)-1; } if($a<=$b){ push @nlist,[@list[$a..$b]]; } } return @nlist; }
    I've done some quick benchmarking and all 3 of the methods posted so far are very equivalent when it comes to performance (tests below are on a 1000 element array arranging it into sub-arrays of 5 elements each)
    submitterbenchmark
    adam6 wallclock secs ( 5.59 usr + 0.00 sys = 5.59 CPU) @ 894.45/s (n=5000)
    lhoward6 wallclock secs ( 5.79 usr + 0.00 sys = 5.79 CPU) @ 863.56/s (n=5000)
    swiftone6 wallclock secs ( 5.71 usr + 0.00 sys = 5.71 CPU) @ 875.66/s (n=5000)
    Testing with other parameters (different list sizes and split sizes) shows different algorithms faster. All algorithms continue to stay very closely grouped performance wise (though I am happy to report that my algorithm appears to be slightly fastest for larger lists).

    I've managed to clean up my code a little (though these changes didn't make it any faster). It doesn't look quite as ugly and still handles the fencepost conditions properly.

    sub reform_list{ my $size=shift; my @list=@_; my @nlist=(); my $list_size=scalar(@list); my $c; my $stop=int($list_size/$size)*$size; for($c=0;$c<$stop;$c+=$size){ push @nlist,[@list[$c..($c+$size-1)]]; } if($c<$list_size){ push @nlist,[@list[$c..($list_size-1)]]; } return @nlist; }
      lhoward and zzamboni are the

      greatest!!!!

      THANK YOU VERY MUCH!!!!

      You are both lifesavers. swiftone and adam, thank you very much as well.
        lhoward and zzamboni are the greatest!!!!

        You are welcome. Particularly since I did not post a reply to your question :-)

        --ZZamboni

        ps. Easy on the fonts there.

Re: list to LoL
by swiftone (Curate) on Jun 02, 2000 at 21:20 UTC
    Untested, but....
    sub lolize { my ($size, $inarrayref)= @_; my $count=0; my @temparray=(); my @returnarray=(); foreach (@$inarrayref){ push @temparray, $_; $count++; if($count==$size){ print @temparray, "\n"; push @returnarray, [@temparray]; @temparray=(); $count=0; } } return [@returnarray]; }
    You'd call it with &lolize($count, $listref);
RE: list to LoL
by Adam (Vicar) on Jun 02, 2000 at 21:21 UTC
    I'm still working on some code, but I think you could do it with a loop and splice.

    Ok, here is my code:

    @A=(1,2,3,4,5,6,7,8,9); # some array $n = 3; # some n while( @B = splice @A, $i++, $n ) { @A= ( [@B], @A ) }; @A = reverse @A; # To put it back in the right order.

    And yes, that blows away the old @A.

      Very true, but since you are dealing with a reference, splice would clobber your original list.

      The code for a splice loop should look something like:

      sub lolize{ my($size, $arrayref)=@_; my @returnarray=(); my $count=0; while($count<=$@arrayref){ push @returnarray, [splice(@$arrayref,$count, $count+$size-1)] +; $count+=$size; } return [@returnarray]; }
      This is untested, so this might "fill-in" your array to fill in the last list. To correct that, you say:
      push @returnarray, [splice(@$arrayref,$count, ($count+$size-1<=@$array +ref)? $count+size-1 :@$arrayref )];
      Hmm. There are probably some fencepost errors and such when $size=1 and suchlike, but this should let you play with it.
Re: list to LoL
by Maqs (Deacon) on Jun 02, 2000 at 21:53 UTC
    Below is Adam's modified version. It seemed to be working well
    my @a = (1,2,3,4,5,6,7,8,9,10,11,12); my $n = 4; #parameter my @lol; while (@a){ @lol = (@lol, [splice @a, 0, $n]); $i=$i+$n; }; print "$lol[2][2]\n"; #for example


    /Maqs.
      I don't know why you are crediting me with that code, I didn't write it. I see no advantages to it except that it eliminates the need to call reverse(). It also has a useless $i (which $^W doesn't warn you about because you do it twice. Mine has that warning but its easily circumvented - after all you know it isn't a typo). Mine uses $i to avoid steping on the refs (note that @A is both the source and destination in my version). It still destroys @A because splice does that. The only way to preserve the original is make a back up copy.
Re: list to LoL
by merlyn (Sage) on Jun 06, 2000 at 07:56 UTC