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

Hi,
I have a hash with different keys, but some values are the same. Is there a way to print each value, and, if I find multiple keys that share the same value, print them all?
Thanks!
  • Comment on Find common values in a hash and print the respective keys

Replies are listed 'Best First'.
Re: Find common values in a hash and print the respective keys
by johngg (Canon) on May 26, 2013 at 19:12 UTC

    Reverse the hash into a HoA and select those with multiple values in the array.

    $ perl -MData::Dumper -Mstrict -Mwarnings -E ' my %h = ( aa => 1, bb => 2, cc => 3, dd => 4, ee => 2, ff => 5, gg => 3, hh => 2, ); my %r; push @{ $r{ $h{ $_ } } }, $_ for keys %h; print Data::Dumper->Dumpxs( [ \ %r ], [ qw{ *r } ] ); say qq{Value $_ present in keys @{ [ join q{, }, @{ $r{ $_ } } ] } } f +or grep { @{ $r{ $_ } } > 1 } keys %r;' %r = ( '1' => [ 'aa' ], '4' => [ 'dd' ], '3' => [ 'gg', 'cc' ], '2' => [ 'bb', 'ee', 'hh' ], '5' => [ 'ff' ] ); Value 3 present in keys gg, cc Value 2 present in keys bb, ee, hh $

    I hope this is helpful.

    Cheers,

    JohnGG

Re: Find common values in a hash and print the respective keys
by LanX (Saint) on May 26, 2013 at 18:51 UTC
    Two different questions I don't understand.

    Do you want to print all keys for the same value?

    Try building a reversed hash of arrays with push @{$reverse{$val}}, $key

    Please show desired input and output and what you tried and I'm sure someone will help you.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Find common values in a hash and print the respective keys
by hdb (Monsignor) on May 26, 2013 at 18:58 UTC

    If you want to avoid to build a hash of arrays, you could concatenate the key strings with a delimiter that does not exist in the values. Something like

    my %rev; while( my( $key, $val) = each %hash ) { $rev{$val} .= "|$key"; }
      Which is quite clever since keys must be strings. =)

      May I propose to put the delimiter at the end (splitting ignores by defaults empty fields at the end) ...

      DB<101> split /\|/, 'a|b|c|' => ("a", "b", "c") DB<102> split /\|/, '|a|b|c' => ("", "a", "b", "c")

      and to chose something less vulnerable like "\0" or "$;" as delimiter?

      Cheers Rolf

      ( addicted to the Perl Programming Language)

Re: Find common values in a hash and print the respective keys
by kcott (Archbishop) on May 26, 2013 at 21:33 UTC

    Here's a technique to achieve this.

    $ perl -Mstrict -Mwarnings -E ' my (%x, %y) = (a => 1, b => 2, c => 3, d => 1, e => 2, f => 1); push @{$y{$x{$_}}}, $_ for keys %x; say "$_: ", join ", ", @{$y{$_}} for keys %y; ' 1: a, d, f 2: b, e 3: c

    I'll leave you to format the output however you want it.

    Take note of the following excerpt from the keys documentation:

    "The keys of a hash are returned in an apparently random order."

    The output shown above was not modified to appear sorted! Here's two more runs also with unmodified output:

    $ perl -Mstrict -Mwarnings -E ' my (%x, %y) = (a => 1, b => 2, c => 3, d => 1, e => 2, f => 1); push @{$y{$x{$_}}}, $_ for keys %x; say "$_: ", join ", ", @{$y{$_}} for keys %y; ' 2: e, b 1: d, a, f 3: c
    $ perl -Mstrict -Mwarnings -E ' my (%x, %y) = (a => 1, b => 2, c => 3, d => 1, e => 2, f => 1); push @{$y{$x{$_}}}, $_ for keys %x; say "$_: ", join ", ", @{$y{$_}} for keys %y; ' 3: c 1: f, d, a 2: b, e

    Update: As ++LanX points out (below), this may not be the clearest example. Here's, hopefully, a better one. This doesn't change the logic, just the clarity.

    $ perl -Mstrict -Mwarnings -E ' my %orig = (a => 1, b => 2, c => 3, d => 1, e => 2, f => 1); my %temp; push @{$temp{$orig{$_}}}, $_ for keys %orig; say "$_: ", join ", ", @{$temp{$_}} for keys %temp; ' 1: a, d, f 3: c 2: b, e

    -- Ken

      Is this more golfing or obfuscation regarding %y ? =)

          my (%x, %y) = (a => 1, b => 2, c => 3, d => 1, e => 2, f => 1);

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        While neither golfing nor obfuscation was intended, your point is well taken: I've updated my node.

        Thanks (and ++ when the Vote Fairy next visits).

        -- Ken

Re: Find common values in a hash and print the respective keys
by Laurent_R (Canon) on May 26, 2013 at 18:52 UTC

    Not exactly sure of what you want to do, but as a starting point, you could sort the keys on the values and then print together the keys where the values match.

      Thanks guys,
      I figured it out, using array of hashes!
      Cheers!
Re: Find common values in a hash and print the respective keys
by Anonymous Monk on May 27, 2013 at 00:30 UTC
    The invert() function from the Var::Pairs module can also do what you want:
    my %your_hash = ( aa => 1, bb => 2, cc => 3, dd => 4, ee => 2, ff => 5, gg => 3, hh => 2, ); use Var::Pairs; my %keys_for_each_value = invert(%your_hash); use Data::Dumper 'Dumper'; warn Dumper \%keys_for_each_value;

      Here's a version optimized for clarity and verisimilitude.

      #!perl # # farm.pl use strict; use warnings; # Lookup table of farm animals by home my %home_of = ( Cat => 'House', Cow => 'Field', Dog => 'Yard', Goat => 'Barn', Hamster => 'House', Hen => 'Coop', Horse => 'Barn', Sheep => 'Barn', Pig => 'Sty', Rooster => 'Yard', ); my %animals_by; # Add each animal to a list of farm animals by home... while (my ($animal, $home) = each %home_of) { push @{ $animals_by{$home} }, $animal; } # Print ordered lists of animals by an ordered list of homes... for my $home (sort keys %animals_by) { my $list_of_animals = join ', ', sort @{ $animals_by{$home} }; print "$home\t$list_of_animals\n"; } __END__ Barn Goat, Horse, Sheep Coop Hen Field Cow House Cat, Hamster Sty Pig Yard Dog, Rooster