I have a simple hash, and would like to return the $key based on $value criteria. That is, for line 14, what code would I need to return the $key where the $value is "yellow"?
1 #!/usr/bin/perl
2
3 # This program creates a hash then
4 # prints out what is in the hash
5
6 %fruit = (
7 'apple' => ['red','green'],
8 'kiwi' => 'green',
9 'banana' => 'yellow',
10 );
11
12 print "The apple is @{$fruit{apple}}.\n";
13 print "The kiwi is $fruit{kiwi}.\n";
14 print "What is yellow? ";
grep
is the right tool for this job:
my @all_matches = grep { $fruit{$_} eq 'yellow' } keys %fruit;
print("$_ ") foreach @matching_keys;
my ($any_match) = grep { $fruit{$_} eq 'yellow' } keys %fruit;
I'm not so sure that's easy to do efficiently with a one-way hash. The whole point of a hash is to convert the key into a value (or position of the value if you're looking under the covers). You can do an exhaustive search over all the values, collecting the keys as you go but that's not as efficient as a hash lookup.
In order to go the other way efficiently, you might want to consider a two-way hash, something like:
%fruit = (
'apple' => ['red','green'],
'kiwi' => 'green',
'banana' => 'yellow',
);
%antifruit = (
'red' => 'apple',
'green' => ['apple','kiwi'],
'yellow' => 'banana',
);
print "The apple is @{$fruit{'apple'}}.\n";
print "The kiwi is $fruit{'kiwi'}.\n";
print "A yellow thing is $antifruit{'yellow'}.\n";
sub find_key {
my ( $h, $value ) = @_;
while ( my ( $k, $v ) = each %$h ) {
return $k if $v eq $value;
}
return;
}
So you could call it like so:
find_key( \%fruit, 'yellow' );
Since some of your values are arrays, you need to check for that.
Calling:
my @fruit = getfruit(\%fruit, $colour);
The subroutine:
sub getfruit {
my ($fruit, $col) = @_;
my @result;
for my $key (keys %$fruit) {
if (ref $fruit->{$key} eq 'ARRAY') {
for (@{$fruit->{$key}}) {
push @result, $key if /^$col$/i;
}
} else {
push @result, $key if $fruit->{$key} =~ /^$col$/i;
}
}
return @result;
}
Using a regex instead of eq is optional, just be mindful of keeping the same case, since Yellow
and yellow
are considered different keys.
I note your example has references to anonymous arrays, so I would just do a long winded foreach/if loop:
my %fruit = (
'apple' => ['red','green'],
'kiwi' => 'green',
'banana' => 'yellow',
);
print "The apple is @{$fruit{apple}}.\n";
print "The kiwi is $fruit{kiwi}.\n";
print "What is yellow? ";
my $ele;
my $search = 'yellow';
my @match = ();
foreach $ele (keys(%fruit)) {
if(ref($fruit{$ele}) eq 'ARRAY' and
grep { $_ eq $search } @{ $fruit{$ele} }) {
push(@match, $ele);
} elsif(!ref($fruit{$ele}) and $fruit{$ele} eq $search) {
push(@match, $ele);
}
}
print join(", ", @match) . "\n";