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

I am not sure what to title this thread.

I would like to print the 10 most recent values accoding the the epoch in a hash, but epoch is not the key.

I guess I could put all of the epochs into an array. Sort the array. Then use another loop to find the key in the hash that corrosponds to the oldest epoch. Then set that key to undef. Then I would have to sort the hash again by epoch to print it. It seems that the solution would be to sort the hash by epoch and remove the last element but I dont know how to do it.

I am not sure what to call this type of hash. Therefore, I dont know what keywords to put in the title. Am I creating a hash of a hash?

Maybe I could store the values as $out_hash{$url}[0]...??

$MAX_HASH_SIZE = 10; #print "Title = " . $out_hash{$url}{TITLE} . "\n"; #print "EPOCH = " . $out_hash{$url}{EPOCH} . "\n"; #print "DESC = " . $out_hash{$url}{DESC} . "\n"; my $size = keys %out_hash; # remove the oldest element from the hash if ($size == $MAX_HASH_SIZE){ foreach $url (keys %out_hash){ push (@t,$out_hash{$url}{EPOCH}); } @t = sort { $a <=> $b } @t; # the oldest epoch should be at the + end. # now find that key in the hash that has the oldest epoch and d +elete it. foreach $url (keys %out_hash){ if ($out_hash{$url}{EPOCH} == $t[$MAX_HASH_SIZE -1]){ $out_hash{$url} = undef; # Will that remove that key? } } } #### Now I need to sort the hash by epoch to print. #### It would be much nicer if I could sort the hash by the epoch. #### and only keep and print the most recent 10 keys.
  • Comment on How to print the 10 most recent (epoch) keys in a hash when epoch is not the key
  • Download Code

Replies are listed 'Best First'.
Re: How to print the 10 most recent (epoch) keys in a hash when epoch is not the key
by BrowserUk (Patriarch) on Nov 28, 2009 at 08:29 UTC

    Build an array of the keys ordered (descending) by the epoch; pop and delete until there are just 10 left; and use the remaining to output in order:

    #! perl -slw use strict; use Data::Dump qw[ pp ]; my $now = time; my %hash = map{; "url$_" => { title => "title $_", EPOCH => $now - 50 + int( rand 100 ), DESC => "Desc $_ ", } } 1 .. 12; pp \%hash; my @epochOrderKeys = sort { $hash{ $b }{ EPOCH } <=> $hash{ $a }{ EPOCH } } keys %hash; delete $hash{ pop @epochOrderKeys } while @epochOrderKeys > 10; print "$_ : ", pp $hash{ $_ } for @epochOrderKeys; __END__ c:\test>809852 { url1 => { DESC => "Desc 1 ", EPOCH => 1259396887, title => "title 1 +" }, url10 => { DESC => "Desc 10 ", EPOCH => 1259396825, title => "title +10" }, url11 => { DESC => "Desc 11 ", EPOCH => 1259396896, title => "title +11" }, url12 => { DESC => "Desc 12 ", EPOCH => 1259396905, title => "title +12" }, url2 => { DESC => "Desc 2 ", EPOCH => 1259396822, title => "title 2 +" }, url3 => { DESC => "Desc 3 ", EPOCH => 1259396833, title => "title 3 +" }, url4 => { DESC => "Desc 4 ", EPOCH => 1259396873, title => "title 4 +" }, url5 => { DESC => "Desc 5 ", EPOCH => 1259396824, title => "title 5 +" }, url6 => { DESC => "Desc 6 ", EPOCH => 1259396833, title => "title 6 +" }, url7 => { DESC => "Desc 7 ", EPOCH => 1259396827, title => "title 7 +" }, url8 => { DESC => "Desc 8 ", EPOCH => 1259396910, title => "title 8 +" }, url9 => { DESC => "Desc 9 ", EPOCH => 1259396843, title => "title 9 +" }, } url8 : { DESC => "Desc 8 ", EPOCH => 1259396910, title => "title 8" } url12 : { DESC => "Desc 12 ", EPOCH => 1259396905, title => "title 12" + } url11 : { DESC => "Desc 11 ", EPOCH => 1259396896, title => "title 11" + } url1 : { DESC => "Desc 1 ", EPOCH => 1259396887, title => "title 1" } url4 : { DESC => "Desc 4 ", EPOCH => 1259396873, title => "title 4" } url9 : { DESC => "Desc 9 ", EPOCH => 1259396843, title => "title 9" } url3 : { DESC => "Desc 3 ", EPOCH => 1259396833, title => "title 3" } url6 : { DESC => "Desc 6 ", EPOCH => 1259396833, title => "title 6" } url7 : { DESC => "Desc 7 ", EPOCH => 1259396827, title => "title 7" } url10 : { DESC => "Desc 10 ", EPOCH => 1259396825, title => "title 10" + }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      delete $hash{ pop @epochOrderKeys } while @epochOrderKeys > 10;
      can also be written as
      $#epochOrderKeys = 9 if $#epochOrderKeys > 9;

      It doesn't remove the records from %hash, but there's no need to. The following would proceed to remove them if so desired:

      %hash = @hash[ @epochOrderKeys ];
      Thanks!
Re: How to print the 10 most recent (epoch) keys in a hash when epoch is not the key
by gmargo (Hermit) on Nov 28, 2009 at 08:20 UTC

    Simple way to get all the hash keys in epoch sorted order:

    my @keys_in_epoch_order = map { $$_[1] } # isolate key sort { $$b[0] <=> $$a[0] } # sort by epoch numerical +descending map { [ $out_hash{$_}{EPOCH}, $_ ] } # arrays of epoch, key keys %out_hash; # source keys
      Simple way to get all the hash keys in epoch sorted order:

      Not quite simple enough! You don't need the added complication of a Schwartzian transform here as the item you are sorting on is already available rather than being obtained via some expensive transaction. Here a ST only slows things down.

      In the following two benchmarks we are sorting files by their epoch modification date. In the first we have a hash of files as key and their epoch modification times as value which are sorted both via direct hash lookup and via a ST. Note how the ST is slower.

      use strict; use warnings; use File::Find; use Benchmark qw { cmpthese }; my %files = (); my $rcWanted = sub { $files{ $File::Find::name } = ( lstat $File::Find::name )[ 9 ]; }; find( $rcWanted, q{/usr/bin} ); cmpthese( -5, { Direct => sub { my $raSorted = useDirect() }, ST => sub { my $raSorted = useST() }, } ); sub useDirect { my @sorted = sort { $files{ $b } <=> $files{ $a } } keys %files; return \ @sorted; } sub useST { my @sorted = map { $_->[ 0 ] } sort { $b->[ 1 ] <=> $a->[ 1 ] } map { [ $_, $files{ $_ } ] } keys %files; return \ @sorted; }

      The output.

      Rate ST Direct ST 35.8/s -- -37% Direct 56.9/s 59% --

      If we now change the code for the second benchmark so that we just have an array of filenames, we have to do the "expensive" inode look-up to get the modification time as part of the sort code. Now the ST makes sense as it avoids the repetitive lstat calls for each file as the sort algorithm moves the file into its correct position.

      use strict; use warnings; use File::Find; use Benchmark qw { cmpthese }; my @files = (); my $rcWanted = sub { push @files, $File::Find::name; }; find( $rcWanted, q{/usr/bin} ); cmpthese( -5, { Direct => sub { my $raSorted = useDirect() }, ST => sub { my $raSorted = useST() }, } ); sub useDirect { my @sorted = sort { lstat( $b ) <=> lstat( $a ) } @files; return \ @sorted; } sub useST { my @sorted = map { $_->[ 0 ] } sort { $b->[ 1 ] <=> $a->[ 1 ] } map { [ $_, lstat( $_ ) ] } @files; return \ @sorted; }

      The output.

      Rate Direct ST Direct 1.29/s -- -46% ST 2.38/s 85% --

      I hope this is of interest.

      Cheers,

      JohnGG

      I thought about joining the 3 values into a string and then pushing them onto an array but I would need something to join/split the values that could never be found on a webpage. Example:

      #To store the values $str = $epoch . "SpLiTvAlUe" . $url . ""SpLiTvAlUe" . $desc; # To print the values unshift (@array, $str); if (@array > 10){ pop(@array) } foreach (@array){ my ($epoch, $url, $desc)= split (/\"SpLiTvAlUe\"/); print $epoch . " " . $url . " " . $desc . "\n"; }
      I have not tried it and I don't like my splitting value but this might work?
      Thanks
Re: How to print the 10 most recent (epoch) keys in a hash when epoch is not the key
by salva (Canon) on Nov 28, 2009 at 19:26 UTC
    Sort::Key::Top does it:
    use Sort::Key::Top qw(rukeytop); # ru => means reverse order and sorting keys are unsigned integers my @recent = rukeytop { $out_hash{$_}{EPOCH} } 10 => keys %out_hash;