[comp.lang.perl] Sorting an associative array by value?

rkrebs@fantasy.dsd.es.com (Randall Krebs) (04/06/91)

In article <1991Apr5.183701.21325@javelin.sim.es.com> pashdown@javelin.sim.es.com (Pete Ashdown) writes:

   How do I go about sorting an associative array, not by the string, but by
   the associated value?  For example, how would I change following 'word count'
   program to sort by occurance rather than alphabetically?

   (From the Nutshell Book)

   while (<>) {
	   s/-\n//g;
	   tr/A-Z/a-z/;
	   @words = split (/\W*\s+\W*/, $_);
	   foreach $word (@words) {
		   $wordcount {$word}++;
	   }
   }

   foreach $word (sort keys(%wordcount)) {
	   printf "%20s %d\n", $word, $wordcount{$word};
   }

Try:

sub freqsort
{
    $wordcount{$a} <=> $wordcount{$b};
}

foreach $word (sort(freqsort keys(%wordcount)))
{
    printf "%20s %d\n", $word, $wordcount{$word};
}

randall.
--
   Randall S. Krebs                        | "If you don't drink, don't drive.
   (rkrebs@dsd.es.com)                     |  And if you don't read the man
   Evans & Sutherland Computer Corporation |  page, don't run the program."
   Salt Lake City, Utah (Where?)           |          - Sister Admin

pashdown@javelin.sim.es.com (Pete Ashdown) (04/06/91)

How do I go about sorting an associative array, not by the string, but by
the associated value?  For example, how would I change following 'word count'
program to sort by occurance rather than alphabetically?

(From the Nutshell Book)

while (<>) {
	s/-\n//g;
	tr/A-Z/a-z/;
	@words = split (/\W*\s+\W*/, $_);
	foreach $word (@words) {
		$wordcount {$word}++;
	}
}

foreach $word (sort keys(%wordcount)) {
	printf "%20s %d\n", $word, $wordcount{$word};
}
-- 
      /       "Uhh" - Jon Bon Jovi         \       HARHARHARHARHARHARHARHARHAR
\\  /  AMGIA!!  The computer for the mind. \X v//  HAR(This space for rent)HAR
 |XV       [FREE CD'S! SEND ME MONEY!]       X\/   HARHARHARHARHARHARHARHARHAR
Pete Ashdown  pashdown@javelin.sim.es.com ...uunet!javelin.sim.es.com!pashdown

tchrist@convex.COM (Tom Christiansen) (04/06/91)

From the keyboard of pashdown@javelin.sim.es.com (Pete Ashdown):
:How do I go about sorting an associative array, not by the string, but by
:the associated value?  For example, how would I change following 'word count'
:program to sort by occurance rather than alphabetically?
:
:foreach $word (sort keys(%wordcount)) {
:	printf "%20s %d\n", $word, $wordcount{$word};
:}

The most straight-forward way is to declare a function that 
given keys, compares values.  For example:

    sub by_value { $wordcount{$a} <=> $wordcount{$b}; } 

    foreach $word (sort by_value keys %wordcount) {
	    printf "%20s %d\n", $word, $wordcount{$word};
    }

A minor problem with this method is that the array to compare
is hardcoded in.  You can avoid this by using dynamic scoping.
I'll also use pass-by-name for efficiency.

    sub _by_value { $table{$a} <=> $table{$b}; } 

    sub sort_by_value {
	local(*table) = @_;
	sort _by_value keys %table;
    } 

    foreach $word (&sort_by_value(*wordcount)) {
	    printf "%20s %d\n", $word, $wordcount{$word};
    }

Another possibilty, which I don't care for as much, is to dynamically
declare the &by_value function using an eval.  This is left as
an exercise for the reader.  :-)

I guess I'd better keep the sorting section in my perl 
tutorial after all.

--tom

merlyn@iwarp.intel.com (Randal L. Schwartz) (04/06/91)

In article <1991Apr5.183701.21325@javelin.sim.es.com>, pashdown@javelin (Pete Ashdown) writes:
| 
| How do I go about sorting an associative array, not by the string, but by
| the associated value?  For example, how would I change following 'word count'
| program to sort by occurance rather than alphabetically?
| 
| (From the Nutshell Book)
| 
| while (<>) {
| 	s/-\n//g;
| 	tr/A-Z/a-z/;
| 	@words = split (/\W*\s+\W*/, $_);
| 	foreach $word (@words) {
| 		$wordcount {$word}++;
| 	}
| }
| 
| foreach $word (sort keys(%wordcount)) {

foreach $word (sort bywordcount keys(%wordcount)) {

| 	printf "%20s %d\n", $word, $wordcount{$word};
| }

sub bywordcount {
	$wordcount{$a} <=> $wordcount{$b}; # use spaceship operator for numeric
}

See the sort operator description on page 180 of The Book.

@a=('hacker,',Just,Perl,another);@a=grep($_=splice(@a,$_,1),1,2,1,0);print"@a"
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Intel: putting the 'backward' in 'backward compatible'..."====/