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

i got what i thought was a wonderful  grep {} map () trick from a previous post ( abstraction -- level up! ), but today, my brain must be mush, because i can't get results.

i'm trying to get an arrayref from this:

my $parts = [ 'Part1', 'Part2','Part3' ]; my $newLoop = [ grep { $_->{SELECTED}++ if $_->{PARTNAME} eq $row->{title}, $_ } map +{ 'PARTNAME' => $_ }, @$parts ] ;
but Data::Dumper is showing me that  $newLoop is empty ...

what i expect:

the map call returns an array of hashrefs
grep runs through the array returned and sets the "selected" element ...
but i'm missing something, and my eyes aren't seeing it ...

Replies are listed 'Best First'.
Re: map and grep syntax question
by Joost (Canon) on Dec 10, 2004 at 21:39 UTC
    ouch. if you'd used warnings you'd get a warning about the "if" statement. The problem is that your comma at the end of the if() statement tries to combine $_ in the same statement. It doesn't really make much sense, and I'm not sure what the perl interpreter makes of it either. Anyway, you want a semicolon there:
    #!/usr/local/bin/perl -w use strict; use Data::Dumper; my $row = { title => 'Part1' }; my $parts = [ 'Part1', 'Part2','Part3' ]; my $newLoop = [ grep { $_->{SELECTED}++ if $_->{PARTNAME} eq $row->{title}; $_ } map +{ 'PARTNAME' => $_ }, @$parts ] ; warn Dumper($newLoop); __END__ $VAR1 = [ { 'SELECTED' => 1, 'PARTNAME' => 'Part1' }, { 'PARTNAME' => 'Part2' }, { 'PARTNAME' => 'Part3' } ];
    update: Actually, your use of grep() is not at all idiomatic: you should probably not modify $_ in the grep block at all: it would be cleaner to use map() instead and return a modified copy.

    update2: demonstration:

    #!/usr/local/bin/perl -w use strict; use Data::Dumper; my $row = { title => 'Part1' }; my $parts = [ 'Part1', 'Part2','Part3' ]; my $newLoop = [ map +{ 'PARTNAME' => $_, $row->{title} eq $_ ? ('SELECTED' => 1) : (), }, @$parts ] ; warn Dumper($newLoop);
      thanks for update2. it's what i needed, and i hadn't even thought of doing the check in the list construction using map

      I think it would be a bit more readable like this:

      my $newLoop = [ map {$_ eq $row->{title} ? {'PARTNAME' => $_, 'SELECTED' => 1} : { +'PARTNAME' => $_} } @$parts ] ; #or even my $newLoop = [ map { if($_ eq $row->{title}) { {'PARTNAME' => $_, 'SELECTED' => 1} } else { {'PARTNAME' => $_} } } @$parts ] ;

      Jenda
      We'd like to help you learn to help yourself
      Look around you, all you see are sympathetic eyes
      Stroll around the grounds until you feel at home
         -- P. Simon in Mrs. Robinson

Re: map and grep syntax question
by revdiablo (Prior) on Dec 10, 2004 at 22:00 UTC

    I'm as big a fan of grep and map as the next guy, but sometimes they just don't make sense. In this case, I think a plain for loop would be clearer:

    my $parts = [ 'Part1', 'Part2','Part3' ]; my $newLoop; for (@$parts) { my $hash = { PARTNAME => $_ }; $hash->{SELECTED}++ if $_ eq $row->{title}; push @$newLoop, $hash; }

    Or, if anything, just a single map call, but this demonstrates why I prefer the for loop:

    my $parts = [ 'Part1', 'Part2','Part3' ]; my $newLoop = [ map {{ PARTNAME => $_, $_ eq $row->{title} ? (SELECTED => 1) : () }} @$parts ];

    Update: Joost beat me to the punch on the single-map part, but I still prefer my first snippet.

      the straight map loop is the most concise, i think, plus i need to work more with some of the added list ops.

      i just want to extend my working knowledge of everything.

        the straight map loop is the most concise, i think

        The most concise solution is not always the best. Take a look at any Perl Golf contest for conclusive proof of this. :-) For example, here's another concise version:

        my $newLoop; push @{$newLoop},{PARTNAME=>$_,/$row->{title}/?(SELECTED=>1):()} for qw(Part1 Part2 Part3);

        But if I saw that in real code, I wouldn't be very happy.

        plus i need to work more with some of the added list ops. i just want to extend my working knowledge of everything.

        That's fine, but part of learning how to work with something is learning when not to use it. I can't say the map verion that Joost and I both came up with is the worst code I've seen, but it isn't particularly clear. So, learn how to use map, but also learn when to use something else.

Re: map and grep syntax question
by gaal (Parson) on Dec 10, 2004 at 21:38 UTC
    No matter what, $newLoop should be an arrayref. If by "empty" you mean undef, something is seriously broken. If you mean arrayref to an empty array...

    ...Then your map has only one possible key, so it can have only one element. Apparently, this isn't enough to match the grep, so you get an empty list.

    Update: Er, no, map returns a list, not a hash. I tend to forget that :) All odd-numbered elements will be identical, though.