Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Test for the last element in a foreach

by tdevil76 (Beadle)
on Aug 02, 2007 at 20:01 UTC ( #630377=perlquestion: print w/ replies, xml ) Need Help??
tdevil76 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks, What is the cleanest(i.e. short and sweet) way to know if you are working on the last element of the array in a foreach? Thanks!

UPDATE: Sorry - I should be more specific - It isn't an array per se, but a function that is returning an array, i.e.:

foreach $subcat ($category->children) {
      # is this the last element?
}

Comment on Test for the last element in a foreach
Re: Test for the last element in a foreach
by ikegami (Pope) on Aug 02, 2007 at 20:14 UTC

    In the CB, I presented:

    for my $i (0..$#array) { for ($array[$i]) { ... if ($i == $#array) { ... } ... } }

    Using this method, no change will be required to the body of your loop. $_ is still aliased to the array element as it normally is in a foreach loop. Changes to it will be reflected in the array.

Re: Test for the last element in a foreach (==\)
by tye (Cardinal) on Aug 02, 2007 at 20:16 UTC

    Since you said "array", you can use \$_ == \$array[-1]:

    my @array= ( 1..5 ); for( @array ) { if( \$_ == \$array[-1] ) { print "Last element.\n"; } print $_, $/; } __END__ 1 2 3 4 Last element. 5

    Update: ikegami asked about Data::Alias being able to confuse this test. Yes, that can be done, and one can even do it w/o such a module but it requires some unusual contortions and so probably isn't something to worry too much about:

    my $five= 5; *array= sub { \@_ }->( 1, 2, $five, 4, $five ); for( @array ) { if( \$_ == \$array[-1] ) { print "Last element.\n"; } print $_, $/; } __END__ 1 2 Last element. 5 4 Last element. 5

    - tye        

Re: Test for the last element in a foreach
by almut (Canon) on Aug 02, 2007 at 20:21 UTC

    Yet another variant:

    my @elems = qw(a b c d e); my $n = $#elems; for my $elem (@elems) { print "last elem = $elem\n" if !$n--; }

    Update: as to your updated question: if you want to avoid creating a named array at any price, you could do something like this (but I'm not sure if I should seriously recommend that...)

    my $n; for my $elem (map {$n++; $_} qw(a b c d e)) { print "last elem = $elem\n" if !--$n; }
Re: Test for the last element in a foreach
by CountZero (Bishop) on Aug 02, 2007 at 20:30 UTC
    I was thinking of:
    my @array = (1 .. 10); my $counter; foreach my $element (@array) { print "Last element follows now!\n" if ++$counter == scalar(@ar +ray); print "$element\n"; }

    Output:

    1 2 3 4 5 6 7 8 9 Last element follows now! 10

    You can leave out the scalar function but then you have to take care that the test does not get evaluated in list context.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Test for the last element in a foreach (list)
by tye (Cardinal) on Aug 02, 2007 at 20:47 UTC

    With your update, you could just assign the returned list to a named array. Or, for fun, do something crazy like:

    sub children { return ( 1..5 ); } my( $end, $subcat ); foreach my $next ( children(), $end ) { if( ! $end ) { $end= 1; $subcat= $next; next; } if( \$next == \$end ) { print "Last element.\n"; } print $subcat, $/; $subcat= $next; } __END__ 1 2 3 4 Last element. 5

    - tye        

Re: Test for the last element in a foreach
by wind (Priest) on Aug 03, 2007 at 00:39 UTC
    If you care about the position of elements in an array, you should simply iterate them by index instead of by element.
    my @children = $category->children; for my $i (0..$#children) { my $subcat = $children[$i]; my $is_last = $i == $#children; # More code here. }
    There is nothing particularly elegant or fancy about this solution. In fact, it's down right boring. However, it's very clear what purpose each variable serves. And so is very maintainable.

    There are other abstractions that you could use that are more both elegant or obfuscated. One good way would be to use an Iterator class. However, this needlessly adds more dependencies to something that is honestly very basic, so I at least wouldn't bother.

    - Miller
Re: Test for the last element in a foreach
by NetWallah (Abbot) on Aug 03, 2007 at 04:23 UTC
    This seems the simplest/cleanest to me:
    my $LastSubCat; foreach my $subcat ($category->children) { $LastSubCat = $subcat; # Code to use $subcat } # $LastSubCat now contains the value of the last element

         "An undefined problem has an infinite number of solutions." - Robert A. Humphrey         "If you're not part of the solution, you're part of the precipitate." - Henry J. Tillman

Re: Test for the last element in a foreach
by mvc (Scribe) on Aug 03, 2007 at 13:29 UTC
    No need for a test on each iteration:
    my @children = $category->children; my $last_child = pop @children; foreach $child (@children) { handle_not_last_child($child); } handle_last_child($last_child);
      Nice mvc++, I like your solution.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (6)
As of 2014-08-23 05:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (172 votes), past polls