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

Hi, 2nd question here.... I've got a hash whose keys are arrays & I want to get the values of a certain slice & print them out. So it's more complicated than if the keys were just scalars because I have to check each element of each array but I think the printing-out part isn't so bad. Is grep still the right function though? Like

my @wants = grep { something } keys %haystack; print Dumper{@haystack{@wants}};

or maybe

print "@haystack{@wants}\n";

Or would I need to use a for or an if first?

edit: for reference this is a continution of this earlier question
http://perlmonks.org/?node_id=1027571

Replies are listed 'Best First'.
Re: question about what to grep(?)
by Don Coyote (Hermit) on Apr 12, 2013 at 11:46 UTC

    Checkout Filter

    I will expect you mean the values of the keys of the hash are arrays, which is a common structure. For selecting, grep is a good operator. You also want to understand how to access the arrays to carry out the conditional test.

    To build an array of the keys of arrays held by the keys of baskets which have 10 items or less:

    my %baskets = ( ten => [0..9], bat => [0..8,'bat:-:laser-disruptor'], elf => [0..10], ); my @tenorless = grep { @{ $baskets{$_} } =< 10 } keys %baskets; # print @tenorless # batten

    In this example, we access the array by providing the hashvalue through the key and dereferencing it with the array sigil and curly braces. Obtaining the scalar context of number of items.

    You can now happily process the baskets in your 10 items or less queue, the baskets with more having been laser blasted to smithereens.

    Of the baskets holding the requisite number of items, some contain goods other than groceries, such as hero equipment, which need to be checked out in the kickass department. Now we really need to access the items inside the arrays. To do this we pass in our newly formed list to a further grep. Similar to adding further foreach loops.

    All we need to watch for is assigning the special variable $_ in each 'outer' grep we use so we can repel magnetic pulse traps and displace gravity gracefully.

    #!usr/bin/perl use warnings; use strict; my @batgoods = map {'bat:-:'.$_}qw(night-visor laser-disruptor zipwire +-refill); my %baskets = ( ten => [0..9], bat => [0..8,'bat:-:laser-disruptor'], elf => [0..10], ); my @tenorless = grep { @{$baskets{$_}} <= 10 } keys %baskets; my @hasbat = grep { my $key = $_; grep { my $batitem = $_; grep {$_ =~ $batitem} @{$baskets{$key}}; } @batgoods; } @tenorless; print @hasbat; # bat exit 0;

    grep, foreach for heros and heroines!

Re: question about what to grep(?)
by Anonymous Monk on Apr 12, 2013 at 04:13 UTC

    I've got a hash whose keys are arrays

    No you don't, thats impossible, keys can only be strings

Re: question about what to grep(?)
by AnomalousMonk (Archbishop) on Apr 12, 2013 at 10:20 UTC
Re: question about what to grep(?)
by hdb (Monsignor) on Apr 12, 2013 at 06:31 UTC

    I think you need to provide a bit more detail about the keys of your hash. As pointed out, they cannot be arrays.

    Apart from that your approach seems to make sense. The something in your grep would be some condition to select some keys from your hash.

    The best approach to print depends on the values of your hash. @haystack{@wants} gives you an array of the values of the selected keys. You have not told us what these values are. They could be simple numbers or complex data structures and you would need to taylor your approach to that. Data::Dumper should always give you a printout but the question is whether it is sufficient for your purpose.

      I've gone with hdb's suggestion in the other node with the nested foreach loops but I simplified the hash definition to

      $haystack{"$a, $b, $c"} =  \@zeros;

      I reversed the hash after reading that in perl, hashes are optimized for searching the keys, so now the \@zeros are the keys. But they're arrays, at least in my head. I'm used to thinking in terms of vectors, sets of ordered pairs, etc & not strings or whatever.

      I'm interested in the keys that only contain integers so the "something" I was thinking of using a regex like this

      / (.*\.0000.*)|(.*\.9999.*) /

      because that would find floats with the right form. It's just I'm not sure what to put in the grep to make sure I'm getting only @zeros that have ALL integers inside & not just at least one. I wouldn't hve thought to use more than one grep like Don Coyote though. Maybe I need to learn more about grep... & then after finding a slice with the right keys, print out the corresponding values. But it's the grep part that I'm stuck on.

        I do not think that reversing the hash helps. In any case you should define a function that returns true (ie 1) if the argument is (approximately) an integer and then do a grep on the \@zeroes.

        # untested sub is_approximately_an_integer { my $x = shift; my $eps = 0.0001; return abs( $x-round($x) ) < $eps; }