http://qs321.pair.com?node_id=121066

moodster has asked for the wisdom of the Perl Monks concerning the following question: (arrays)

If I have a list like this one:
@list = qw( a b c d e f );
I can easily loop through each element with a simple for loop like this:
for $el (@list) { print "$el\n"; }
But suppose the elements in the loop are grouped two and two and that for each loop I want to get two elements. Something like this (I know the syntax is all wrong):
for $el1, $el2 (@list) { print "$el1 - $el2\n"; }
I know there must be an easy way to do this (this being perl and all), but I can't figure it out. Anyone?

Originally posted as a Categorized Question.

Replies are listed 'Best First'.
Re: How do I loop through a list two or more elements at a time?
by davorg (Chancellor) on Oct 24, 2001 at 14:41 UTC

    If you don't mind destroying your array in the process, you can use splice.

    while (my ($el1, $el2) = splice(@list, 0, 2)) { # do stuff }
Re: How do I loop through a list two or more elements at a time?
by tachyon (Chancellor) on Oct 24, 2001 at 21:18 UTC

    {cough} you could always use the obvious approach with a C style loop

    @ary = qw( a b c d e f g h i ); for ( my $i=0; $i<@ary; $i=$i+2 ) { print "$ary[$i] - $ary[$i+1]\n"; }
Re: How do I loop through a list two or more elements at a time?
by jeroenes (Priest) on Oct 24, 2001 at 15:37 UTC
    For a real answer now: create your list as an array of arrays, loop on the outer array.
    print "$_->[0] - $_->[1]\n" for @LoL;

    If you start with a flat list, you create the LoL with davorg's solution:

    my @LoL; while(my @items = splice( @list, 0, 2) ){ push @LoL, [@items]; }
    or non-destructive:
    my $size = 2; die "Uneven list" if @list % $size; my @LoL; for my $index ( 0.. $#list ){ my $rem = $index % $size; $LoL[($index - $rem)/$size]->[$rem] = $list[$index]; }
Re: How do I loop through a list two or more elements at a time?
by petral (Curate) on Oct 25, 2001 at 20:58 UTC
    To be pedantic, the question was "two or more".
    Here's a conventional answer which allows for varying the grouping size. (Just to make it a little interesting, I included an alternate method for dealing with the last group being short.)
    my@a=qw(a b c d e f); my $grpsiz = 2; if (@a% $grpsiz) { push @a, ("") x ($grpsiz - @a % $grpsiz) } for (my$i = 0; $i <= $#a; $i += $grpsiz) { print @a[$i .. $i+$grpsiz-1] }
Re: How do I loop through a list two or more elements at a time?
by tachyon (Chancellor) on Oct 26, 2001 at 01:11 UTC
    my @ary = qw(a b c d e f g); @_ = @ary; while (my ($one,$two) = (shift @_, shift @_)) { print "$one $two\n"; last unless defined $two; }

    Originally posted as a Categorized Answer.

Re: How do I loop through a list two or more elements at a time?
by jeroenes (Priest) on Oct 24, 2001 at 14:38 UTC
    my %hash = @list; print "$_ - $hash{$_}\n" for keys %hash;

      my %hash = @list will stringify the first of every pair of elements in the array, as well as remove all but the last of every pair, the first elements of which stringify to the same "key".

      ... plus, it is not easily generalizable to more than two elements at a time ...

      Okay, I guess I should make a suggestion as well:

      for (map{{el1=>$list[2*$_], el2=>$list[2*$_+1]}} 0 .. $#list/2) { # do things with $_->{el1} and $_->{el2} }

      In most cases this will be enough though (but I would not trust it with continue blocks):

      for (my $i=0 ; $i < @list ; $i+=2) { my ($el1, $el2) = @list[$i,$i+1]; # do things with $el1 and $el2 }

      Neither method aliases the list elements; to do that nicely you need a tie() or two. Or just work with references to or directly upon $list[$i] and $list[$i+1].

      The Sidhekin

Re: How do I loop through a list two or more elements at a time?
by duff (Parson) on Feb 04, 2006 at 07:00 UTC

    In perl6 you'd just use the built-in facilities of your ordinary for loop:

    for @array -> $a, $b { ... } for @array -> $one, $two, $three { ... }

    Due to some regularization of the language, for loops are aware of the arity of the code block, and pulls off as many elements as needed. This also works for map as well:

    my @bytwo = map -> $a, $b { [$a,$b] } @array;
      You can also use natatime from List::MoreUtils:
      my @x = ('a' .. 'g'); my $it = natatime 2, @x; while (my ($x,$y) = $it->()) { print "$x $y\n"; }
Re: How do I loop through a list two or more elements at a time?
by tachyon (Chancellor) on Oct 26, 2001 at 01:08 UTC
    my @ary = qw(a b c d e f g); @_ = @ary; while (my ($one,$two) = (shift @_, shift @_)) { print "$one $two\n"; last unless defined $two; }
Re: How do I loop through a list two or more elements at a time?
by jffry (Hermit) on Feb 04, 2006 at 00:04 UTC
    Just now I needed a +=3 list loop, and I made a hash of anonymous arrays:
    my %hash = ( 'one' => [ '1', 'first' ], 'beta' => [ '2', 'second' ], 'zeta' => [ '-1', 'last' ], ); while ((my ($key, $value)) = each %hash) { a_func_w_3_args($key, $value->[0], $value->[1]); }
    The big plus for me in this format was that it makes the step 3 iteration of the data painfully obvious. Hopefully, the code isn't too painful, tho.