Your variables are poorly named. Reusing $states for the state name? ouch.
my $find = "ISO2:4166-23:US";
my $found_state_name;
while ( my ($country_name, $states) = each(%$VAR1) ) {
while (my ($state_name, $state) = each(%$states) ) {
while ( my ($key, $value) = each(%$state) ) {
if ($value eq $find) {
$found_state_name = $state_name;
}
}
}
}
Now, it would be nice to stop searching as soon as you a result is found. We can't do that while still using each(cause it'll screw up later keys/values/each on those hashes).
my $find = "ISO2:4166-23:US";
my $found_state_name;
FIND:
for my $country_name (keys(%$VAR1)) {
my $country = $VAR1->{$country_name};
for my $state_name (keys(%$country)) {
my $state = $country->{$state_name};
for my $key (keys(%$state)) {
if ($state->{$key} eq $find) {
$found_state_name = $state_name;
last FIND;
}
}
}
}
We never use $country_name or $key except to get the value.
my $find = "ISO2:4166-23:US";
my $found_state_name;
FIND:
for my $states (values(%$VAR1)) {
for my $state_name (keys(%$states)) {
my $state = $country->{$state_name};
for my $value (values(%$state)) {
if ($value eq $find) {
$found_state_name = $state_name;
last FIND;
}
}
}
}
If you know you're looking for an ISO2 value, this simplifies to the following:
my $find = "ISO2:4166-23:US";
my $found_state_name;
FIND:
for my $states (values(%$VAR1)) {
for my $state_name (keys(%$states)) {
my $state = $states->{$state_name};
if ($state->{ISO2} eq $find) {
$found_state_name = $state_name;
last FIND;
}
}
}
You want to use grep, eh? Since you want a state name as a result, we need to grep a list of state names.
my @state_names = ...;
my ($found_state_name) =
grep { ... }
@state_names;
We can obtain the list of state name using
my @state_names =
map { keys(%$_) }
values(%$VAR1);
But that's not quite enough to perform the check. (For now, I'm going to assume that only the ISO2 property needs to be checked.)
my @state_names =
map { keys(%$_) }
values(%$VAR1);
my ($found_state_name) =
grep { $VAR1->{???}{$_}{ISO2} eq $find }
@state_names;
There are two solutions. You can work with country-state pairs.
my @country_state_name_pairs =;
map {
my $country_name = $_;
map { [ $country_name, $_ ] }
keys(%{ $VAR1->{$country_name} )
}
keys(%$VAR1);
my ($found_state_name) =
map { $_->[1] }
grep {
my ($country_name, $state_name) = @$_;
$VAR1->{$country_name}{$state_name}{ISO2} eq $find
}
@country_state_name_pairs;
Or you can create a flat lists of states and search that.
my @states_with_name =
map { [ $_, $VAR1->{$_} ] }
values(%$VAR1);
my ($found_state_name) =
map { $_->[0] }
grep { $_->[1]{ISO2} eq $find }
@states_with_name;
Noting stops us from merging the two statements.
my ($found_state_name) =
map { $_->[0] } # Get the state name.
grep { $_->[1]{ISO2} eq $find } # Filter out undesireable states.
map { [ $_, $VAR1->{$_} ] } # $state_name => [ $state_name, $state ]
values(%$VAR1); # Get the countries.
This last one isn't too bad!
Finally, there are two ways to modify each of the above to search all the fields instead of just ISO2). (I'm going to only the modifications to the latter of the above two solutions.)
my ($found_state_name) =
map { $_->[0] } # Get the state name.
grep { # Filter out undesireable states.
grep { $_ eq $find } # Filter out undesireable properties of the state.
values(%{ $_->[1] }) # Get the state's property values.
}
map { [ $_, $VAR1->{$_} ] } # $state_name => [ $state_name, $state ]
values(%$VAR1); # Get the countries.
or
my ($found_state_name) =
map { $_->[0] } # Get the state name.
grep { $_->[1] eq $find } # Filter out undesireable states.
map { # $state_name => multiple [ $state_name, $value ]
my $state_name = $_;
map { [ $state_name, $_ ] } # value => [ $state_name, $value ]
values(%{ $VAR1->{$_} ) # Get the state's property values.
}
values(%$VAR1); # Get the countries.
These aren't readable. They are best avoided.
Finally, if you are going to perform many searches based on ISO2, it would be best if you organized your data in terms of ISO2.
my %by_iso2 = (
'ISO2:4166-23:US' => {
country_name => 'USA',
state_name => 'Alabama',
ISO2 => 'ISO2:4166-23:US',
ISO3 => 'ISO3:3166-2:US',
...
},
'ISO2:4166-23:US' => {
country_name => 'USA',
state_name => 'Washington',
ISO2 => 'ISO2:30-23:US',
ISO3 => 'ISO3:40-166-2:US',
...
},
...
);