Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Sorting in foreach 2

Status
Not open for further replies.

Masali

Programmer
Jun 19, 2002
143
0
0
SE
Hi!

I have a hash that looks something like this:
[tt]
key1: 4
key2: 1
key3: 19
key4: 8
key5: 14
[/tt]
Then I am going to foreach this hash but I would like it in reverse number order using the values 4, 1, 19, 8, 14.

Something like this:
Code:
foreach my $key(keys %Sets) #some sorting gizmo...
{
print "$key: $Sets{$key}\n";
}
So the output from such a foreach would be:
[tt]
key3: 19
key5: 14
key4: 8
key1: 4
key2: 1
[/tt]
Thanks in advance

Masali
 
A hash is not a good choice of data structure if you need the data in order. Think hashes for random access (via keys); think arrays for order.

Having said that, there are 2 ways to go that I can think of.

1. If you know you will not have duplicate values in your hash, you can reverse the hash into another hash, so that the values become the keys of the new hash and the former keys become its values, then sort in reverse order on the new keys, like so:
Code:
my %Sets2 = reverse(%Sets);
for my $k (sort {$b <=> $a} keys %Sets2) {
    print "$Sets2{$k} => $k\n";
}
Output:
key3 => 19
key5 => 14
key4 => 8
key1 => 4
key2 => 1


Do not do this if there's a chance you may have duplicate values in the hash. Since the values become keys you will lose data if there are any duplicate values.

2. The other method is to put the key/value pairs into an array and then sort the array, i.e.,
Code:
my @temp = sort {$b->[1] <=> $a->[1]} 
           map{[$_, $Sets{$_}]} keys %Sets;

print "$_->[0] => $_->[1]\n" for @temp;
Same output as the first example.

To reiterate, if you need the data in order, a hash is probably not a good choice in the first place.

HTH

 
Actually, for #2 above, if you won't be needing the @temp array for anything else, you could do without it:
Code:
for (sort {$b->[1] <=> $a->[1]} map{[$_, $Sets{$_}]} keys %Sets) {
    print "$_->[0] => $_->[1]\n";
}
That's kind of a mouthful, though. Maybe clearer with the @temp variable. :)


 
my %set = (
"key1" => "4",
"key2" => "1",
"key3" => "19",
"key4" => "8",
"key5" => "14",
"key6" => "14"

);
foreach my $key (sort {$set{$b} <=>$set{$a}} keys %set ) {
print "$key = $set{$key}\n";
}

output :

key3 = 19
key5 = 14
key6 = 14
key4 = 8
key1 = 4
key2 = 1


Thanks
Dinesh
 
Code:
foreach my $key(keys %Sets) {
   push @temp, sprintf("%0.4d",$Sets{$key})." | $key";  
                                     #sort on value,not key
}
@temp2=reverse(sort(@temp));         #sort the array, and then reverse
foreach (@temp2) {
  my($value, $key)=split /\|/, $_;   #split the values, 
  print "$key=>$value\n";            #and print
}
TOIMWTDI
--Paul

It's important in life to always strike a happy medium, so if you see someone with a crystal ball, and a smile on their face ... smack the fecker
 
A minor variation on Dinesh's (19decsat) answer using a sub for clarity:
Code:
foreach (sort byVal keys %hash) {
    print "$_ $hash{$_}\n";
}

sub byVal {
    return $hash{$b} <=> $hash{$a};
}
Cheers, Neil
 
Thank you all for you great answers!

I found the solution that toolkit gave me as the best for my application!


Thanks again
Masali
 
19decsat, I like your solution best for simplicity. I see now that I was really making things too complicated. Doh!
Have a star.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top