Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using map to extract hash set of (key, value) matching specified value

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.

like image 542
Thomas B in BDX Avatar asked Apr 08 '11 15:04

Thomas B in BDX


2 Answers

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!"

like image 73
Axeman Avatar answered Oct 03 '22 19:10

Axeman


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.

like image 43
Eric Strom Avatar answered Oct 03 '22 17:10

Eric Strom