http://qs321.pair.com?node_id=1142996

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

I have an AoH that looks like this:
my @aoh = ( {k0 => 'v0' } , {k1 => 'v1' } , {k2 => 'v2' } , {k3 => 'v3' } );
I want to pull out the value of the element with key k2. The nicest way I can think of to do this is something like
$val = [grep {(keys $_)[0] eq 'k2'} @aoh]->[0]{'k2'};
But that's kind of disgusting. Is there a nicer way?

Update: Corion pointed out that I could just convert the AoH into a hash given the keys are unique and have a single value each. Like:

my %h = map {%$_} @aoh; ## now its just: say $h{'k2'};


Replies are listed 'Best First'.
Re: extracting elements by key from an array of hashes
by choroba (Cardinal) on Sep 25, 2015 at 09:58 UTC
    If the keys are distinct over the hashes, the question is why to store them in an array.
    my %hash = ( k0 => 'c0', k1 => 'v1', k2 => 'v2', k3 => 'v3', ); my $search = 'k2'; my $val = $hash{$search};

    Even if the keys aren't unique, you might want to store the data in a HoA instead:

    my %hash = ( k0 => ['c0'], k1 => ['v1'], k2 => ['v2', 'v42'], k3 => ['v3'], ); my $search = 'k2'; my @vals = @{ $hash{$search} };
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
      True that. However, the data is coming from an xmlrpc source via XML::RPC and that's how it's structured. I don't have control over the source API or at least I don't want to change it.

        You can easily create a temporary hash to do the searches:
        my %hash = map %$_, @rpc;
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

        Keep in mind that you have no obligation to store data internally in inconvenient formats. If it is more convenient to manipulate it internally as a hash, do that conversion at the earliest possible point after reading it in, and convert back at the latest possible point if you ever have to output the structure again.


        Dave

        ... and if there is potentially more than one value per bucket, simply make each element an arrayref. Auto-vivification makes this trivially easy to do.
Re: extracting elements by key from an array of hashes
by Discipulus (Canon) on Sep 25, 2015 at 10:37 UTC
    If you have a big data you can also try (even if ambiguous, equivocal, devious..)
    grep defined, map {$_->{'k2'}} @aoh;

    L*
    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

      Hello Discipulus,

      grep defined, map {$_->{'k2'}} @aoh;

      This works well as long as the hash value is itself defined. But if the hash entry exists with a value of undef:

      my @aoh = ( { k0 => 'v0' }, { k1 => 'v1' }, { k2 => undef }, { k3 => 'v3' }, );

      then the result of the grep will be indistinguishable from the case in which the hash entry does not exist at all. The following does distinguish between these two cases:

      map { exists $_->{k2} ? $_->{k2} : () } @aoh;

      Hope that helps,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Another cleaner variant that works with the initial data structure.
        my @aoh = ( {k0 => 'v0' } , {k1 => 'v1' } , {k2 => 'v2' } , {k3 => 'v3' } ); my ($val) = grep {exists $_->{k2}} @aoh; $val &&= $val->{k2};
        yes, i was aware of that possibility. thanks for your code. Perl can be incredibly expressive!

        L*
        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.