http://www.perlmonks.org?node_id=11105969

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

Hi Monks,

Could you tell me how can I fetch array of values from hashref in single instruction? Thanks.

my @a = qw/c b d/; my %h = ( a => 4, b => 3, c => 2, d => 1, ); my @b = @h{@a}; # this one works as expected print "2 3 1\n", Dumper(\@b), "\n"; my $h = \%h; my @c = @{%$h}->{@a}; # this one fails, what is the correct syntax? print "2 3 1\n", Dumper(\@c), "\n";

Replies are listed 'Best First'.
Re: Fetch array of values from hashref
by AnomalousMonk (Bishop) on Sep 10, 2019 at 14:46 UTC

    c:\@Work\Perl\monks>perl -wMstrict -MData::Dumper -le "my @a = qw/c b d/; ;; my %h = ( a => 4, b => 3, c => 2, d => 1, ); ;; my @b = @h{@a}; print \"2 3 1\n\", Dumper(\@b), \"\n\"; ;; my $hr = \%h; my @c = @{ $hr }{ @a }; print \"2 3 1\n\", Dumper(\@c), \"\n\"; " 2 3 1 $VAR1 = [ 2, 3, 1 ]; 2 3 1 $VAR1 = [ 2, 3, 1 ];

    Update: Oops... Fixed example code: Was dumping  @b twice!


    Give a man a fish:  <%-{-{-{-<

Re: Fetch array of values from hashref
by Eily (Monsignor) on Sep 10, 2019 at 15:27 UTC

    AnomalousMonk showed you the all-weather syntax. But in later version of perls you can also use Postfix Dereference Syntax:

    perl -E "$h = {a=>1, b=>2, c=>3, d=>4}; @k = qw(b d); say for $h->@{@k +};" 2 4
    You can see ->@{ } as a "Slice on a ref" operator. I like the fact that it prevents having nested parts both on the accessed variable and the accessors itself (or said another way, you only need one set of {}). But this syntax is less well known and understood (and not always consistent with the rest of the perl syntax).

    If the hash is not too big though, copying the content of %$h into %temp and then using the @temp{@keys} format is maybe the less confusing option though.

Re: Fetch array of values from hashref
by LanX (Archbishop) on Sep 10, 2019 at 16:28 UTC
    Rule of thumb for Perl:

    • Replace the symbol with the ref!
    • Respect precedence
  • h is the symbol in %h
  • $h_ref is the ref

    demo:

    DB<1> @a = c..d DB<2> @h{a..d}=1..4 DB<3> $h_ref = \%h DB<4> x $h_ref # dump all 0 HASH(0x3531868) 'a' => 1 'b' => 2 'c' => 3 'd' => 4 DB<5> x @h{@a} 0 3 1 4 DB<6> x @{$h_ref}{@a} # play it safe 0 3 1 4 DB<7> x @$h_ref{@a} # brackets not needed b/c precedence already r +espected 0 3 1 4 DB<8>

    HTH! :)

    PS: Side note: "hashslice from hashref" is the technical term for "Fetch array of values from hashref"

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Fetch array of values from hashref
by k-mx (Acolyte) on Sep 11, 2019 at 07:30 UTC
    I think that code must be expressive, so it's nice when you use slices this way:
    my $h = { foo => 1, bar => 2 }; @{ $h }{qw/ foo bar /} = ( 667, 889 ); # or postfix form: # $h->@{qw/ foo bar /} = ( 667, 889 );
    But for your problem, more clear solution would be:
    my @ar = values %$h; # or postfix form: # my @ar = values $h->%*;
    You can read more about slices here:
    perldoc perldata perldoc perlref
      But for your problem, more clear solution would be:
      my @ar = values %$h;

      However, note this form is not equivalent: the order of the returned values will be random.

      ... for your problem ...

      I would go a bit further than haukex and say that the problem exemplified in the OP is to extract the values of a subset | an ordered subset of hash keys, whereas  values %$h gives you the values of all keys (in random order!). Again, not an equivalent solution.

      Update: As a side note, WRT hash key/value ordering it's worth mentioning that keys, values, and each all state that:

      So long as a given hash is unmodified you may rely on keys, values and each to repeatedly return the same order as each other. See Algorithmic Complexity Attacks in perlsec for details on why hash order is randomized.
      The actual random ordering will differ from one run of a script to another if this security feature is enabled. (IIRC, this feature, enabled by default, can be disabled during the build of a Perl interpreter.)


      Give a man a fish:  <%-{-{-{-<

        Yes, my bad. I thought he needed all values.

        P.S. I want to share another cool hack, maybe somebody don't know about it:

        ( \$Param{Foo}, \$Param{Bar} ); \( $Param{Foo}, $Param{Bar} ); \@Param{'Foo', 'Bar'};
        You can use it in situations like this
Re: Fetch array of values from hashref
by pme (Prior) on Sep 10, 2019 at 20:29 UTC
    Thank you all for your answers.