Reputation:
I have the value of a multi-dimensional hash in Perl.
Its structure is
$hash{$key}{$field}{$date} = $value;
Given that I have both the correct value and field.
What is the fastest process possible to search for its key given that the value itself is unique (has a 1-1 relationship to the key)
EDIT:
I have added a third level which is date. not all dates have a value but when it does, it is shared through all the dates.
To simplify it, if it has a value, it is "A", else, blank.
Regards, InnZaayynn
Upvotes: 0
Views: 670
Reputation: 385915
The organization of your data is not suited to do a fast search. You're going to have to iterate through the entire hash. If you're going to perform multiple searches, it's best if you generate the "inverse" hash so you only need to iterate through the entire hash once instead of once per search.
If you are performing multiple searches, and they're not all for the same field, generate the inverse hash as follows:
my %key_by_field_and_value;
for my $key (keys(%hash)) {
my $hash_for_key = $hash{$key};
for my $field (keys(%$hash_for_key)) {
my $hash_for_key_and_field = $hash_for_key->{$field};
defined( my $date = get_any_one_key($hash_for_key_and_field) )
or next;
length( my $value = $hash_for_key_and_field->{$date} )
or next;
$key_by_field_and_value{$field}{$value} = $key;
}
}
Then, a search becomes
my $field = ...;
my $target_value = ...;
if (defined(
my $target_key =
do { no autovivification; $key_by_field_and_value{$field}{$target_value} }
)) {
...
}
If you are performing multiple searches, and they're all for the same field, generate the inverse hash as follows:
my $field = ...;
my %key_by_value;
for my $key (keys(%hash)) {
my $hash_for_key = $hash{$key};
defined( my $hash_for_key_and_field = $hash_for_key->{$field} )
or next;
defined( my $date = get_any_one_key($hash_for_key_and_field) )
or next;
length( my $value = $hash_for_key_and_field->{$date} )
or next;
$key_by_value{$value} = $key;
}
Then, a search becomes
my $target_value = ...;
if (defined( my $target_key = $key_by_value{$target_value} )) {
...
}
If you're just going to search once, you'll have to search the entire hash.
my $field = ...;
my $target_value = ...;
my $target_key;
for my $key (keys(%hash)) {
my $hash_for_key = $hash{$key};
defined( my $hash_for_key_and_field = $hash_for_key->{$field} )
or next;
defined( my $date = get_any_one_key($hash_for_key_and_field) )
or next;
length( my $value = $hash_for_key_and_field->{$date} )
or next;
if ($value eq $target_value) {
$target_key = $key;
last;
}
}
if (defined($target_key)) {
...
}
Both of the above solutions use this efficient version of my ($key) = keys(%$h);
:
sub get_any_one_key {
my ($h) = @_;
my $key = each(%$h);
keys(%$h); # Reset iterator
return $key;
}
Upvotes: 3