Reputation: 2800
I have been looking for a way to do the following action using the perl function map
:
given a hash, I want to extract the pairs (key, value) in which the value equals or matches a specified parameter.
In my example I want to extract the (key, value) pairs where value = failed
, but it could also be an expression (i.e. string starting with A, or a REGEX). That's why I want as a result a hash and not only a table of the keys matching the value.
my %strings = (
bla => "success",
ble => "failed",
bli => "failed",
foo => "success",
blo => "failed",
bar => "failed",
blu => "success"
);
my %failed_s = ();
while (my ($k, $v) = each %strings) {
if ( $v eq 'failed' ) {$failed_s{$k} = $v};
};
I tried several ways of doing this, but without great results so I think I'm getting confused about references, affectation, results, etc.
my %failed_s =
map { { $_ => $strings{$_} }
if ( $strings{$_}./failed/ ) }
keys %strings;
my %failed_s =
map { ( $strings{$_} eq 'failed') &&
($_, $strings{$_}) }
keys %strings;
print "Dumper \%failed_s: \n" . Dumper(\%failed_s);
It might be not possible or not effective to use map, in this case, but it would help me (and probably others) to know why.
Upvotes: 6
Views: 12279
Reputation: 386541
I think a map+grep combo is cleaner.
my %failed_s =
map { $_ => $strings{$_} }
grep { $strings{$_} eq 'failed' }
keys(%strings);
But since grep can be implemented using map,
my %failed_s =
map { $_ => $strings{$_} }
map { $strings{$_} eq 'failed' ? $_ : () }
keys(%strings);
it can be done using just map.
my %failed_s =
map { $strings{$_} eq 'failed' ? ($_ => $strings{$_}) : () }
keys(%strings);
They problem you had is that you were returning false when you meant to return nothing.
Upvotes: 4
Reputation: 29854
Perl has enjoyed an easy access to hashing logic to such an extent that few people have been able to differentiate the Key-Value Pair concept from its most common implementation: The Hash.
So when most Perlers think about KVP, they think "hash". But think about the way that Perl Arrays are 1) Lists, 2) Queues, and 3) Stacks, and you'll see the implementation is not exactly equal to concept.
I saw a need to treat pairs in a fashion outside of hashes so I created my own library which I meant to upload to CPAN, until I found List::Pairwise
already there.
(Of course it does not seem to have my "hash splicing" methods...)
Anyway, using List::Pairwise
, you'd simply
use List::Pairwise qw<grepp>;
my %failed_s = grepp { $b eq 'failed' } %strings;
I feel like an infomercial guy when I say "Just look how concise that is!"
Upvotes: 9
Reputation: 40152
In your first map example, you are returning single element hash references, which is not what you want.
In your second, the &&
operator imposes scalar context on its arguments, so the list ($_, $strings{$_})
only returns the last item in the list.
What you are looking for is:
my %failed_s = map {
$strings{$_} eq 'failed' ? ($_, $strings{$_}) : ()
} keys %strings;
Where the ? :
operator returns the list if the condition is true, and an empty list if it is false.
Upvotes: 6
Reputation: 118665
map FUNC, LIST
operates on every element in the LIST. Your function could return an empty list for some elements (as Eric Strom's answer suggests). Another approach is to use another filter so that map
only operates on the elements you are interested in:
my %failed_s = map {
$_ => $strings{$_}
} grep { $strings{$_} eq 'failed' } keys %strings;
Upvotes: 3