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

What's the perlish way to get an array which is every other element of another array?

my @evens = @all[#even elements only];

Replies are listed 'Best First'.
Re: alternate array elements
by almut (Canon) on May 20, 2010 at 13:52 UTC
    my @evens = @all[map $_*2+1, 0..$#all/2];
    my @evens = @all[grep !($_ % 2), 0..$#all]; # even-index elements my @odds = @all[grep $_ % 2, 0..$#all]; # odd-index elements

    (update: fixed off-by-one error, in case of uneven number of elements)

Re: alternate array elements
by moritz (Cardinal) on May 20, 2010 at 13:54 UTC
    $ perl -E 'say grep {++$i %2} <a b c d e f g>' aceg $ perl -E 'say grep {$i++ %2} <a b c d e f g>' bdf

    They only nasty thing about this solution is that it requires a variable (here $i) in the outer scope

    A working Perl 6 solution:

    $ perl6 -e 'my @a = "a".."g"; say @a[0, *+2 ... +@a]' aceg

    An idiomatic Perl 6 solution (but not yet implemented in Rakudo):

    @a[0, *+2 ... *]

    Another working Perl 6 solution, which is a variation of the grep theme but without the need of an external variable:

    say <a b c d e f g>.pairs.grep({.key !% 2})>>.value

    (Updated: Added Perl 6 examples)

    Perl 6 - links to (nearly) everything that is Perl 6.
      With the advent of the new state key word in 5.10. I would think this would be the best solution today.
      my @odd = grep {state $i=1; $i++ % 2} qw{a 1 b 2 c 3}; my @even = grep {state $i=0; $i++ % 2} qw{a 1 b 2 c 3};
      Example
      perl -e 'use v5.10; my @odd = grep {state $i=1; $i++ % 2} qw{a 1 b 2 +c 3}; say @odd;'
        BTW: for one-liners you can use global vars like $a or $b without declaration, even when strict.

        C:\>perl -e"print grep {++$a % 2} qw{a 1 b 2 c 3}" abc

        Though their values will leak into the outer scope, so state is better for longer scripts.

        FWIW: I always thought that an automatic loop-count variable $c or $^c would come very handy inside grep-like constructs.

        Cheers Rolf
        (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
        Wikisyntax for the Monastery

Re: alternate array elements
by cdarke (Prior) on May 20, 2010 at 14:01 UTC
    It depends what you mean by even, I am assuming even offsets 0,2,4,6, etc.:
    my @all = qw(zero wun two tree fower fife six seven ait niner); my @evens = @all[grep {not $_ % 2} 0..$#all];

    However I rather like:
    my @evens = keys %{{@all}};
    but (being a hash) the items must be unique, and you loose the original order. If the other values are required (offsets 1,3,5,7) then use values instead of keys.
    Update: removed some superfluous parentheses and changed not, if you see what I mean.
Re: alternate array elements
by toolic (Bishop) on May 20, 2010 at 14:13 UTC
Re: alternate array elements
by jwkrahn (Abbot) on May 20, 2010 at 14:59 UTC

    Another way to do it:

    $ perl -le' use List::MoreUtils "natatime"; my @x = "a" .. "z"; my $iter = natatime 2, @x; my @even; while ( my @y = $iter->() ) { push @even, $y[ 0 ]; } print "@even"; ' a c e g i k m o q s u w y
      Thanks all!

      toolic I didn't know of supersearch so that's useful

      cdarke I like the keys approach, hadn't thought of that at all and presumably it's fast, will try that next time if I don't need to preserve order.

      For posterity, I decided I actually needed the ODD indices (fwiw, the array has duplicate values in it and I'm porting VBA code which uses 1-based arrays, hence want to keep $all[0] as it = undef) so I used:

      @odd = @all[grep($_%2==0, 0..$#all)];

      or seomething like that. I guess the not would be faster tho, but I think it'd confuse my readers more, they'll already be struggling with grep, % and $#!!

        gah should have said evens - basically edited solution by almac at the top now
Re: alternate array elements
by JavaFan (Canon) on May 20, 2010 at 14:41 UTC
    And yet another way. Unlikely to be the fastest.
    my @evens; { last unless @all; shift @all; last unless @all; push @evens, shift @all; redo; }