Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Interchanging hash values

by mjab (Sexton)
on Apr 19, 2003 at 23:29 UTC ( #251735=perlquestion: print w/ replies, xml ) Need Help??
mjab has asked for the wisdom of the Perl Monks concerning the following question:

Greetings, Monks. I'm a struggling novice who has recently completed the exercises in the "Learning Perl" book. In an effort to further my perl knowledge, I've been devising small challenges for myself to complete, most pertaining to hashes (since I'm a bit fuzzy in that regard). I'm now seeking to interchange the smallest hash value of a hash with the largest hash value. Here's my code:
#!/usr/bin/perl -w use strict; use Data::Dumper; my %hash = (Matt => 24, Dana => 19, Eric => 28, Sara => 20, John => 17, Mike => 23,); print Dumper \%hash; my @sort = sort {$hash{$a} <=> $hash{$b}} keys %hash; my $temp = $hash{$sort[scalar @sort - 1]}; $hash{$sort[scalar @sort - 1]} = $hash{$sort[0]}; $hash{$sort[0]} = $temp; print Dumper \%hash;

This accomplishes what I want (interchanging the key John value with the key Eric value), but seems rather cumbersome. Is the array of sorted keys by value (@sort) really necessary? I'm seeking a more elegant solution. Thanks for any help.

Matt

Comment on Interchanging hash values
Download Code
Re: Interchanging hash values
by BrowserUk (Pope) on Apr 19, 2003 at 23:36 UTC

    #!/usr/bin/perl -w use strict; use Data::Dumper; my %hash = ( Matt => 24, Dana => 19, Eric => 28, Sara => 20, John => 17, Mike = +> 23, ); my @sorted = sort{ $hash{$a} <=> $hash{$b} } keys %hash; @hash{ @sorted[0,-1] } = @hash{ @sorted[-1,0] }; print Dumper \%hash;

    Examine what is said, not who speaks.
    1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
    2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
    3) Any sufficiently advanced technology is indistinguishable from magic.
    Arthur C. Clarke.
      Neat. I never considered using array and hash slices. Works perfect. What does the -1 in the array slice signify? The last element of the array? Search turned up a lot of interesting information on array and hash slices which I will peruse. Thank you for your input.

      Matt

        What does the -1 in the array slice signify? The last element of the array?

        Yes. Any negative indices are taken to be the highest position minus the absolute value. So -1 is the last, -2 the second from last and so on. Similarly, negative offsets used in substr, index are offsets from the end of the string, and with splice for arrays.

        This is a very pragmatic and useful feature of perl. there are two extensions to the notion that I wish were supportable.

        I would like -0 (minus zero) to signify beyond the end of the array or string. This would be useful in some algorithms that use substr or splice to insert things relative to the end of the string or array.

        my $s = 'the quick brown fox'; substr($s, 10, 0) = 'light-'; # result: 'the quick light-brow +n fox' substr($s, -3, 0) = 'female '; # result: 'the quick light-brow +n female fox' substr($s, 0, 0) = 'I saw '; # result: 'I saw the quick ligh +t-brown female fox' substr($s, -0, 0) = ' cross the road'; # ' cross the road.I saw the qu +ick light brown female fox' :(

        Unfortunately, in the last example above, the -zero is taken as the same as zero, and the assigned text is prepended to the string rather than appended. There is no syntax available to append to the end of the string using substr. Using -1 would have resulted in 'I saw the quick light-brown fo cross the road.x'. This forces the need to test for special cases:

        sub insert_end_relative{ my ($string, $pos, $insert) = @_; # ADDED the 0 subsequent to Abigail's post below. return $p ? substr($string, -$pos, 0) = $insert : $string .= $insert; }

        Which could be avoided if perl supported the concept of negative zero.

        And the other thing that would be useful, is for 0..-1 to be equivalent to 0..$#array when used in the context of an array slice @array[0..-1] would be slightly nicer than @array[0..$#array] but there are probably two many cases where it would be ambiguous.


        Examine what is said, not who speaks.
        1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
        2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
        3) Any sufficiently advanced technology is indistinguishable from magic.
        Arthur C. Clarke.
Re: Interchanging hash values
by DrManhattan (Chaplain) on Apr 19, 2003 at 23:42 UTC
    There are a couple of ways you can improve on this. First, you can access the last element of an array by using the index -1. I.e.
    $sort[scalar @sort - 1] == $sort[-1];
    Also, you can swap the values of two variables without a temporary variable in the middle:
    ($a, $b) = ($b, $a); # swap the values of $a and $b
    So your code can be rewritten like so:
    #!/usr/bin/perl -w use strict; use Data::Dumper; my %hash = (Matt => 24, Dana => 19, Eric => 28, Sara => 20, John => 17, Mike => 23,); print Dumper \%hash; my @sort = sort {$hash{$a} <=> $hash{$b}} keys %hash; ($hash{$sort[0]}, $hash{$sort[-1]}) = ($hash{$sort[-1]}, $hash{$sort[0]}); print Dumper \%hash;

    -Matt

Re: Interchanging hash values
by dpuu (Chaplain) on Apr 20, 2003 at 01:35 UTC
    The other solutions are nice, but if you want a more efficient algorithm then it is indeed possible to avoid the sort. An O(N) algorithm is:
    #!/usr/bin/perl -w use strict; use Data::Dumper; my %hash = ( Matt => 24, Dana => 19, Eric => 28, Sara => 20, John => 17, Mi +ke => 23, ); if (%hash) { my (@min, @max); while( my ($k,$v) = each %hash ) { @min = ($k,$v) unless @min && ($v > $min[1]); @max = ($k,$v) unless @max && ($v < $max[1]); } $hash{$min[0]} = $max[1]; $hash{$max[0]} = $min[1]; } print Dumper \%hash;
    --Dave
Re: Interchanging hash values
by artist (Parson) on Apr 20, 2003 at 16:32 UTC
    Hi mjab,
    You have received good answers for your question. Since your request is not common, if we know why you want to do that, may be we can propose a better solution.

    artist

      In an effort to further my perl knowledge, I've been devising small challenges for myself to complete

      My guess is mjab just wants to have practice manipulating hashes even if this particular excersise has no practical application.

      Cheers - L~R

Re: Interchanging hash values (List::Util)
by Aristotle (Chancellor) on Apr 21, 2003 at 03:39 UTC
    use strict; use warnings; use List::Util qw(reduce); my $maxkey = reduce { $hash{$a} > $hash{$b} ? $a : $b } keys %hash; my $minkey = reduce { $hash{$a} < $hash{$b} ? $a : $b } keys %hash; @hash{$minkey,$maxkey} = @hash{$maxkey,$minkey};

    Makeshifts last the longest.

      Why make two passes?

      D:\Perl\test>perl use strict; use warnings; my %h; @h{'a'..'j'} = 0..9; print %h, $/; my ($min,$max) = (keys %h); ($min,$max) = ( $h{$min} < $h{$_} ? $min : $_, $h{$max} > $h{$_} ? $ma +x : $_ ) for keys %h; @h{$min,$max} = @h{$max,$min}; print %h, $/; ^Z a0b1c2d3e4f5g6h7i8j9 a9b1c2d3e4f5g6h7i8j0

      Examine what is said, not who speaks.
      1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
      2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
      3) Any sufficiently advanced technology is indistinguishable from magic.
      Arthur C. Clarke.
        Simply for the laziness of using List::Util. But if you're gonna optimize it, why check both conditions? An element can't be the maximum and the minimum at the same time. You're also pulling the list of all keys twice.
        my ($min, $max) = (scalar each %h) x 2; ($min, $max) = ( $h{$_} < $h{$min} ? ($_, $max) : $h{$_} > $h{$max} ? ($min, $_) : ($min, $max) ) for keys %h;
        You could even go all out on each to remove the last bit of redundant work, and to reduce memory footprint.
        my $min = each %h; my $max = each %h; if(defined $max) { local $_; ($min, $max) = ( $h{$_} < $h{$min} ? ($_, $max) : $h{$_} > $h{$max} ? ($min, $_) : ($min, $max) ) while defined($_ = each %h); } else { $max = $min; }
        That's all cool if your hash is really huge. If it's just a couple pairs, I'll prefer the lazy route any day.

        Makeshifts last the longest.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://251735]
Approved by lacertus
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (9)
As of 2014-12-20 16:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (96 votes), past polls