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

Hi Bretheren,
I was hoping the following would enable me to print out from the input percentage from the hash by key / value pairs. The hash is sorted in a specific order, but it is not coming out in that order. I need to be able to hold the UPDATE insertion order to output.
Any ideas will be much appreciated.
my $Percent = <STDIN>; my $per = $Percent/100;#percent as decimal my $pairs = (keys %lineFinal);# pairs of key/value my $counter = 0;# zero counter my $amount_to_print = int($per * $pairs); foreach my $key ( keys %lineFinal) { if ($counter < $amount_to_print) { print $key, ' ', $lineFinal{$key}, "\n"; #print OUTPERC $key, ' ', $lineFinal{$key}, "\n"; print OUTPERC "$key\t $lineFinal{$key}\n"; } else { last; } $counter++; }

Replies are listed 'Best First'.
Re: Cutting the top percentage out of a Hash
by ikegami (Patriarch) on Apr 13, 2006 at 18:38 UTC
    The hash is sorted in a specific order,

    That's impossible. Hashes can't be sorted, as you've noticed. Are you sure an AoA isn't more appropriate here? If you wish to continue using a hash, the typical solution it the sort the data as you read it from the hash, as follows:

    foreach my $key ( sort { ... } keys %lineFinal) { ... }

    Alternatively, you could use Tie::IxHash instead of a hash. You use it just like a normal hash (thanks to tie), but the contents are ordered.

      Alternatively, you could use Tie::IxHash instead of a hash. The syntax is the same (thanks to tie), but the contents are ordered.

      It should be made clear that the order is the insertion order and not an arbitrary sort criteria. While the module does provide rudimentary sort capability, it is limited to asciibetical sorts of the keys or values.

      Tie::Hash::Sorted may be a better fit.

      Cheers - L~R

        Thanks for the info
        I was hoping to avoid using Tie:: Hash
        a) Because I haven't used it before and I'm not sure of syntax.
        b) I was hoping for a more simple solution that would avoid installing the Tie:: Hash module
Re: Cutting the top percentage out of a Hash
by Codon (Friar) on Apr 13, 2006 at 18:35 UTC
    Hashes aren't stored sorted in Perl. You can sort the data bofore you put it in the hash, but the data is stored in an arbitrary order. If you need the contents of the hash sorted, you need to the sort the data as you pull it from the hash.

    Ivan Heffner
    Sr. Software Engineer, DAS Lead, Inc.
Re: Cutting the top percentage out of a Hash
by johngg (Canon) on Apr 13, 2006 at 21:37 UTC
    It is probably a good idea to take the plunge and try new modules as other Monks have suggested. However, I have put a script together to show you one way of preserving your insertion order when using hashes. I have used hash references, sometimes called anonymous hashes, which are useful when building complex data structures. Doing $refToHash = {} creates a reference to an empty hash. They are also useful for passing data into and out of subroutines.

    I have taken a random subset of the X11 rgb.txt file as data source in order to build a hash of colours. They are in no particular order but the order they are in can be preserved. I have also used Data::Dumper so you can see what the data structure looks like after we have constructed it.

    use strict; use warnings; # Use this to show the data structure. # use Data::Dumper; # Initialise a hash reference and a sequence no. # my $rhPreserve = {}; my $insertionOrder = 0; # Read our data line by line, chomping off the newline # and splitting on the ":" and using the colour name # as the key and RGB for the value. # while(<DATA>) { chomp; my ($value, $key) = split /:/; # Make an entry in the hash reference for this colour but # the value is another hash with two key/value pairs, one # for our RGB value and the other the insertion order # which we increment after insertion. # $rhPreserve->{$key} = { value => $value, insertionOrder => $insertionOrder ++ }; } # Use Data::Dumper to print out the hash reference so we can see # what we have constructed. The colour "keys" will come out in an # order determined by the underlying hashing algorith. # my $dd = Data::Dumper->new([$rhPreserve], ["rhPreserve"]); print "Data structure\n", $dd->Dumpxs(); # To use our insertion order, which we have preserved in the hash # reference, we can sort by "insertionOrder" then use the sorted keys # to access the hash reference. # my @keysInInsertionOrder = sort { $rhPreserve->{$a}->{insertionOrder} <=> $rhPreserve->{$b}->{insertionOrder} } keys %$rhPreserve; print "Data in insertion order\n"; foreach (@keysInInsertionOrder) { print "Insertion order - $rhPreserve->{$_}->{insertionOrder}\n", " Key - $_\n", " Value - $rhPreserve->{$_}->{value}\n"; } __END__ 255 250 250:snow 248 248 255:GhostWhite 245 245 245:WhiteSmoke 220 220 220:gainsboro 255 250 240:FloralWhite 253 245 230:OldLace 250 240 230:linen 250 235 215:AntiqueWhite 255 239 213:PapayaWhip 255 235 205:BlanchedAlmond 255 228 196:bisque 255 218 185:PeachPuff 255 222 173:NavajoWhite 255 228 181:moccasin 255 248 220:cornsilk 255 255 240:ivory 255 250 205:LemonChiffon 255 245 238:seashell 240 255 240:honeydew

    When run this produces the following

    I hope this will help.