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

Say I have an array of hashes like this:

@response = [ { 'name' => 'foo', 'id' => 'bar', 'self' => 'baz' }, { 'name' => 'foo2', 'id' => 'bar2', 'self' => 'baz2' }, ]

Now I have a subroutine called filter which I call like this:

my $responseref = \@response; my $filtered_response = filter ($responseref, 'name', 'id');

How would the subroutine need to look to get an array reference as return value of which the array would look like:

@response = [ { 'name' => 'foo', 'id' => 'bar', }, { 'name' => 'foo2', 'id' => 'bar2', }, ]

Ideally it should work without installing any additional modules
It should also be pretty generic, it should for example be able take a different amount of fields to filter through every time

Replies are listed 'Best First'.
Re: Filter array of hashes for specific keys
by Corion (Patriarch) on Oct 21, 2016 at 09:36 UTC

    So what code have you already written and where did you encounter problems? Where does your code produce different results from what you expect and how are they different?

      So far I've tried it myself this way:

      sub filter { die 'Error: no fields to filter through' if (@_ < 2); my $response = shift; my @fields; foreach my $a (0..$#_) { $fields[$a]= shift; } if (ref $response eq 'ARRAY') { my @response = @$response; my @filtered_responsearr; my %filtered_response; foreach my $ref (@response) { my %response = %$ref; foreach my $field (@fields) { $filtered_response{$field}=$response{$field}; } push @filtered_responsearr, %filtered_response; } return \@filtered_responsearr; }

      which returns an array of all the fields, like ['name','foo','id','bar','name','foo2','id','bar2']
      But I don't need an array of all the fields, I need an array of hashes, because I have to work with the hashes afterwards

        You want to push a hash reference and not a hash:

        push @filtered_responsearr, \%filtered_response;

        Your code can be greatly simplified in some other places:

        my $response = shift; my @fields; foreach my $a (0..$#_) { $fields[$a]= shift; }

        can be rewritten as a single assignment:

        my( $response, @fields ) = @_;

        Likewise, copying the elements of %response into the filtered response can be done using a hash slice instead of the loop:

        @filtered_response{ @fields } = @response{ @fields }
        foreach my $a (0..$#_) { $fields[$a]= shift; }
        Avoid using $a and $b as variables, give them other names ;) .
        perlvar -> $a, $b
Re: Filter array of hashes for specific keys
by Athanasius (Archbishop) on Oct 22, 2016 at 03:12 UTC

    Hello ddominnik,

    Say I have an array of hashes like this:

    Actually, what you show is an array of arrays of hashes; the array @response has only one element, an anonymous array containing two hashes. If you really just want an array of hashes (AoH), use parentheses (round brackets) instead of square brackets at the top level:

    @response = ( { name => 'foo', id => 'bar', self => 'baz', }, { name => 'foo2', id => 'bar2', self => 'baz2', }, );

    See perlreftut and perldsc.

    BTW, note that the “fat comma” operator => stringifies its left operand,1 so in this case you don’t have to explicitly quote the key names.

    1 “...if it begins with a letter or underscore and is composed only of letters, digits and underscores.” (perlop#Comma-Operator)

    Hope that helps,

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