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

Hello Perlmonks,
My question is related to Re^4: fetch url
I'm almost there .............

input:
# topic,description,url,hits,
topic,description,url,10,
topic,description,url2,4,
topic,description,url3,6,
topic,description,url4,3,

snippet:
my (%hash); my $data = "$BaseDir/perf.log"; open STUFF, $data or die "cannot open for read $!"; while ( <STUFF>) { chomp; #my $X = "html"; #next if ( $_ !~ /^$X/ ); # Only look at lines begin with th +e topic next if ( $_ !~ /^[a-z]/ ); # Only look at lines begin with +the topic my ($topic, $desc, $url, $hits) = split "," ; if ($topic){ ${hash}{$url}{topic} = $topic if ($topic); ${hash}{$url}{desc} = $desc if ($desc); ${hash}{$url}{url} = $url if ($url); ${hash}{$url}{hits} = $hits if ($hits); } } close STUFF; print Dumper %hash , "\n"; print "Sort Numeric using <=> with supersort\n"; foreach my $sorted_url ( reverse sort { $hash{$a}->{'hits'} <=> $hash{ +$b}->{'hits'} } keys %hash){ print $sorted_url, " has got ", ${hash}{$sorted_url}{hits}, " +hits \n";

output:
Sort Numeric using <=> with supersort
url has got 10 hits
url3 has got 6 hits
url2 has got 4 hits
url4 has got 3 hits

Just we want to have.




Question: I use the same technique in a subroutine
input: (same)
# topic,description,url,hits,
topic,description,url,10,
topic,description,url2,4,
topic,description,url3,6,
topic,description,url4,3,
snippet:
# -|print_topic|------------------------------------------------------ +--------- # Pre : t_topic # Post: # -------------------------------------------------------------------- +--------- sub print_topic ($) { my $t_topic = shift; open( INFILE, "$conf_file" ) or die "$!"; while (<INFILE>) { chomp; my (%hash); next if 0 == length($_); # Solves "Use of uninitialized va +lue in .......... " next if ( $_ !~ /^$t_topic/ ); # Only look at lines begin +with the t_topic my ($topic, $desc, $url, $hits) = split "," ; if ($topic) { ${hash}{$url}{topic} = $topic if ($topic); ${hash}{$url}{desc} = $desc if ($desc); ${hash}{$url}{url} = $url if ($url); ${hash}{$url}{hits} = $hits if ($hits); } #print "Sort Numeric using <=> with supersort\n"; #print Dumper %hash , "\n"; foreach my $sorted_url ( reverse sort { $hash{$a}->{'hits'} <= +> $hash{$b}->{'hits'} } keys %hash){ print $sorted_url, " has got ", ${hash}{$sorted_url}{hits}, "\ +n"; } } close INFILE; }
output: (not sorted by number!!!!!)

topic,description,url,10,
topic,description,url2,4,
topic,description,url3,6,
topic,description,url4,3,
Do you have any clue ?

Thank you in advance
Perlboer

Replies are listed 'Best First'.
Re: sorting hash ref
by cool_jr256 (Acolyte) on Jun 10, 2005 at 14:13 UTC
    Did you notice that every new line you read from your INPUT file, you initialize the 'hash table' ? This way you'll never be able to sort, since at
    any given time you have one value in the hash.
    Secondly you might want to take the 'foreach' loop outside of the 'while loop'. Tried it out and it works, all sorted.
      cool_jr256,

      Gaze (stare) to this code all day, I didn't saw it.
      Thank you very much for your quick response!
      Perlboer.
Re: sorting hash ref
by tlm (Prior) on Jun 10, 2005 at 14:22 UTC

    cool_jr256's post explains what's wrong with your code, so the following is a relatively minor point. There's no need to follow a the sort with reverse. Just reverse the order of the $a/$b comparison:

    foreach my $sorted_url ( sort { $hash{$b}->{'hits'} <=> $hash{$a}->{'hits'} ) keys %hash ) {

    Update: Revised wording slightly.

    the lowliest monk

      reverse sort { my_cmp($a, $b) } @foo # (1)
      and
      sort { my_cmp($b, $a) } @foo # (2)
      are not strictly equivalent: order is reversed for terms comparing as equal.

      For instance:

      @u = map { [$_, $i++] } (0,0,3,2,2); $" = '-'; print( (map {$_->[1]} reverse sort { $a->[0] <=> $b->[0] } @u), "\n", (map {$_->[1]} sort { $b->[0] <=> $a->[0] } @u), "\n" );
      prints
      24310 23401
      though I believe that most of the times, the correct solution is actually the second one or it just doesn't mind at all!
        Your assertion about stable sort ordering is true, though I'd add that you cannot depend on ANY sort order when iterating through the keys of a normal hash.

        Even two successive runs using the same hash full of data may give different ordering, since (1) it may be influenced by insertion order, and (2) it may be influenced by insertion attack protections, and (3) it may be influenced by other changes in the algorithms between versions of perl.

        --
        [ e d @ h a l l e y . c c ]

Re: sorting hash ref
by Samy_rio (Vicar) on Jun 10, 2005 at 14:57 UTC

    Hi,

    use Array::Parallel; @your_ratings = (10, 4, 6, 3); @url = ('url', 'url2', 'url3', 'url4'); $array = Array::Parallel->new(\@your_ratings, \@url); ($your_ratings, $url) = $array->sort('num'); for my $num ( (0 .. $#{@{your_ratings}} ) ) { print "$url->[$num] has got $your_ratings->[$num] hits\n"; }

    Try this, I think it may help you.

        In CPAN, Array::Parallel package is available as

        Array-Sort-0.02.tar