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

I have a hash in Perl where I want to take the in between indexes of a hash and assign it to the next highest hash value. For example my hash is:
my %hash = (-5189 => 63, -3213 => 9, -2357 => 3);
and I want 3000 to be a key that returns 9.

I tried putting the hash into an array, sorting it, and then putting it back into an hash. But I couldn't figure out the correct way to do it. It seems like there could be a function to do this.

Replies are listed 'Best First'.
Re: Setting In-between values in a hash (interpolating)
by dreadpiratepeter (Priest) on Sep 26, 2008 at 21:01 UTC
    off the top of my head:
    my %hash = (-5189 => 63, -3213 => 9, -2357 => 3); sub next_key { my ($h,$key) = @_; foreach my $hkey (sort keys %$h) { return $h->{$hkey} if $hkey >= $key; } return; } print next_key(\%hash,3000) . "\n";
    but it returns null for 3000, because I believe your data is wrong. You imply that the keys should be positive. Try:
    my %hash = (5189 => 63, 3213 => 9, 2357 => 3);


    -pete
    "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
      No need to sort
      sub next_key { my ($h, $key) = @_; my $best; foreach my $hkey (keys %$h) { if ( $hkey >= $key ) { if ( !defined($best) || $hkey < $best ) { $best = $hkey; } } } return $best; }

      If you use one of the many ordered hash implementations out there (Tie::IxHash, Tie::Hash::Indexed, Tie::Hash::Sorted, etc.) your code gets even simpler.

      Untested example:

      use Tie::Hash::Sorted; my %hash = ( 5189 => 63, 3213 => 9, 2357 => 3, ); tie my %sorted_hash, 'Tie::Hash::Sorted', Hash => \%ages, Sort_Routine => sub { $a <=> $b }, ); sub next_key { my ($h,$key) = @_; foreach my $hkey (keys %$h) { return $h->{$hkey} if $hkey >= $key; } return; } print next_key(\%hash,3000) . "\n";

      You should be able to make a tied array.

      A rough outline:

      • Store your data in a hash, with keys as above.
      • Keep an array of sorted keys.
      • Your FETCH method will do lookups using a method like the above or even a binary search of your array of keys.
      • Your STORE method will add an item to the hash, and resort the array.
      • POP and SHIFT should remove and return the highest/lowest elements in the array.
      • Not sure about UNSHIFT or PUSH. Should clearly define all the normal hash behavior.

      Anyhow, this seems like a job for a tied array to me. Yeah you've got to do a lot of crud in the background to make it work, but once you are done, it becomes so easy to use.

      tie my @numbers, 'Tie::Array::NextIndex', Members => { 5189 => 63, 2357 => 3, }; my $value = $numbers[3000]; # value = 63 $numbers[3213] = 9; $value = $numbers[3000]; # value = 9


      TGI says moo

Re: Setting In-between values in a hash (interpolating)
by ikegami (Patriarch) on Sep 26, 2008 at 21:03 UTC

    If you have *lots* of numbers or if you did often, it would be better to use a binary search on a sorted array rather than using a hash.

    dreadpiratepeter'sO(N log N)
    Best hashO(N)
    Best unsorted arrayO(N)
    Binary search
    (must be sorted)
    O(log N)

    Update: Added links.

      lol, didn't know I was being graded ;) Never said it was a good solution, just a solution. My gut feeling is that he is asking the wrong question and there is a better solution for what he is trying to do.


      -pete
      "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
Re: Setting In-between values in a hash (interpolating)
by JavaFan (Canon) on Sep 26, 2008 at 21:03 UTC
    It's unclear what you mean. Given that your hash keys are all negative, how does '3000' be something "in between"? And considering that your values are 3, 9, 63, how does 9 become the "next highest hash value"?
Re: Setting In-between values in a hash (interpolating)
by Fletch (Bishop) on Sep 26, 2008 at 21:09 UTC

    Fuzzy question, but perhaps Tie::Hash::Interpolate?

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.