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

I've read up on sort {$b <=> $a} but some how its not working for me. I have a hash table with the keys = words and values = occurance of words...I'm not getting how I would sort the values. Do I do..
@keys = sort {$b->$word_list{$word} <=> $a->$word_list{$word}} values +%word_list
Any wisdom is appriciated. Thank you.

Replies are listed 'Best First'.
Re: sorting hashes by value
by samtregar (Abbot) on Nov 08, 2002 at 21:20 UTC
    Do you want to sort the values or sort the keys by their values? I suspect the latter. Here's how to get a list of words sorted by value:

    @words = sort { $word_list{$a} <=> $word_list{$b} } keys %word_list;

    This works because keys() returns a list of keys for the %word_list hash. Then the sort block compares these keys by looking up their value in %word_list. The return from sort is the list of keys sorted by their values.

    If you really wanted a list of sorted values, that's just:

    @values = sort values %word_list;

    -sam

      Thanks! so when its sorted into @words does it keep the word and the value? So in order for me to print it do I
      for $i (0.. $#words_list){ for $words (keys %{$words[$i]}){ print "word $i = $words\n"; } }
      ??
        No, that won't work. If you need to keep the values along with the keys you can build it into the sort:

        @pairs = sort { $a->[1] <=> $b->[1] } map { [ $_ => $word_list{$_} ] } keys %word_list; print "word $_->[0] = $_->[1]\n" for @pairs;

        This works by building up a list of two-element arrays using map. The first element is the key and the second element is the value. Then sort is used to sort them by the second element. The return value is a list of two-element arrays, sorted by their second element.

        You might recognize this as the tail of a Schwartzian Transform.

        -sam

        While samtregar is right in his method for keeping the keys and values together, you probably don't need to go to that trouble. Why not just do something like this:
        @words = sort { $word_list{$a} <=> $word_list{$b} } keys %word_list; foreach $word (@words) { print "word $word = $word_list{$word}\n"; }
        @words is already a sorted list of the keys in %word_list. Of course, this assumes that nobody is doing pesky things like adding to %word_list between the time you sorted it and the time you decided to print it.
Re: sorting hashes by value
by Zaxo (Archbishop) on Nov 08, 2002 at 21:22 UTC

    You're near, but if you want to get keys out, you need to put keys in:

    @keys = sort { $word_list{$a} <=> $word_list{$b} } keys %word_list;

    After Compline,
    Zaxo

      To print the values with the words...would it be...
      @keysorted = sort {$word_list{$b} <=> $word_list{$a}} keys %word_list; for $i (0..$#keysorted){ for $word(keys %{$keysorted[$i]}){ pritn "$word = $keys[$i]{word}\n";
        There's an easier way to do it:
        foreach my $key ( sort { $word_list{$b} <=> $word_list{$a} } keys %word_list ) { print $key, " ", $word_list{$key}, "\n"; }
Re: sorting hashes by value
by waswas-fng (Curate) on Nov 08, 2002 at 21:22 UTC
    @orderedkeys = sort {$hashname{"$a"} <=> $hashname{"$b"} } keys %hashname;

    -Waswas
Re: sorting hashes by value
by Theseus (Pilgrim) on Nov 08, 2002 at 22:33 UTC
    Could the Schwartzian Transform be applied to this problem? I'm definitely not the one to write the code for you(way too unfamiliar with the inner workings of the ST for me to start recommending how others should use it), but if I'm not mistaken as to its purpose, it seems like it would work for this application.
      The Schwartzian Transform would work for this application, just like it would for any sort. It is a replacement for a normal sort which trades memory space for speed. For example, imagine you need to sort by the foozle() of each member of a list. You could do it the easy way:

      @result = sort { foozle($a) <=> foozle($b) } @list;

      But the problem is that foozle() will be called multiple times for each item being sorted. If foozle() is a slow function then you'll be wasting a lot of time. The Schwartzian transform works by building up a temporary data structure than contains the value and the foozle() of that value. Example:

      @result = map { $_->[1] } sort { $a->[0] <=> $b->[0] } map { [ foozle($_), $_ ] } @list;

      However, in this case the "foozle()" is just a hash lookup. Hash lookups are so fast that an ST is probably more expensive than a simple sort.

      -sam

Re: sorting hashes by value
by data67 (Monk) on Nov 09, 2002 at 07:13 UTC
    Just a reminder, that Data::Dumper is your friend for stuff like this.

    Goooo Juve!

Re: sorting hashes by value
by Anonymous Monk on Nov 10, 2002 at 01:19 UTC
    You got lots of replies already, one more comment is that, you can either sort the entities as numbers or characters. the order would be different, for example, if you have a list:
    @a = (2, 10, 1); sort {$a <=> $b} @a would give you (1, 2, 10), when sort {$a cmp $b} @a will give you (1, 10, 2)
    the second one is the default.
      Another basic thing about sort you might want to know is that, you can determining the sroting order simply by the oder of $a and $b. If you have
      @a = (1,3,2); sort {$a <=> $b} gives you (1,2,3), when sort {$b <=> $a} gives you (3,2,1)