Reputation: 1944
try to sort nested hash according to its values, long story,i parse %senseHash
and store certain values in %rankedTest
hash
later, i want to loop through sort %rankedTest
and output to file
my code:
foreach my $str (keys %senseHash)
{
my $selectedSense;
if (exists $senseHash{$str}{'phone'} && exists $senseHash{$str}{'product'})
{
if ($senseHash{$str}{'phone'} > $senseHash{$str}{'product'})
{
$selectedSense='phone';
}else
{
$selectedSense='product';
}
my $pPhone = $senseHash{$str}{'phone'} / $senses{'phone'};
my $pProduct = $senseHash{$str}{'product'} / $senses{'product'};
my $rankScore = abs(log($pPhone/$pProduct));
$rankedTest{$str}{$selectedSense}=$rankScore;
}
}
open($f1, '>', $inputs[1]);
foreach my $e (keys %rankedTest)
{
foreach my $s (sort { $rankedTest{$e}{$b} <=> $rankedTest{$e}{$a} } keys %{$rankedTest{$e}})
{
print $f2 "feature:$e -> sense:$s -> score:$rankedTest{$e}{$s} \n";
}
}
but output is something as below :
feature:operations -> sense:product -> score:0.0859904478555225
feature:move -> sense:product -> score:0.309133999169732
feature:dispute -> sense:phone -> score:0.0963311089384321
while what i actually want is :
feature:move -> sense:product -> score:0.309133999169732
feature:dispute -> sense:phone -> score:0.0963311089384321
feature:operations -> sense:product -> score:0.0859904478555225
any clue what i did wrong or how i can accomplish this. any suggestion will help.
EDIT: inside hash has one key which could be phone or product and i want to sort according to what this key value regardless of the key whether phone
or product
Upvotes: 0
Views: 317
Reputation: 66883
To sort a multi-level hash by values in its bottom layer we have to compare all entries.
This code builds strings to be printed for all levels using (double) map, then sorts that
use warnings;
use strict;
use feature 'say';
# Example hash
my %h = (
oper => { prod => 0.8, phone => 0.03 },
disp => { prod => 0.2, phone => 0.05 },
move => { prod => 0.6, phone => 0.01 }
);
my @res =
sort { (split ":", $b)[-1] <=> (split ":", $a)[-1] }
map {
my $k = $_;
map { "feature:$k -> sense:$_ -> score:$h{$k}{$_}" }
keys %{$h{$k}}
}
keys %h;
say for @res;
Output
feature:oper -> sense:prod -> score:0.8 feature:move -> sense:prod -> score:0.6 feature:disp -> sense:prod -> score:0.2 feature:disp -> sense:phone -> score:0.05 feature:oper -> sense:phone -> score:0.03 feature:move -> sense:phone -> score:0.01
I set phone
values to be less than prod
's to mimic posted data (irrelevant for processing).
The example hash %h
stands for %rankedTest
in the question. It may have either or both (or none) of prod
and phone
in subhashes.
Upvotes: 1
Reputation: 126722
Essentially, you're sorting the elements of the wrong hash. Your data structure %rankedTest
looks like this
my %rankedTest = (
operations => { product => 0.0859904478555225 },
move => { product => 0.309133999169732 },
dispute => { product => 0.0963311089384321 },
);
and you're sorting the keys of the inner hashes that look like { product => 0.0859904478555225 }
. But there's only a single element in each of them so your inner for
loop executes only once and sorting makes no difference
Here I've sorted the outer hash by the value of the product
element of the inner hash
Note that it's often simpler to save intermediate results into temporary variables when dealing with complex data structures. Here I've stored the sorted keys in array @features
and the value of the score in $score
to save putting the long hash expression into the print
statement
my @features = sort {
$rankedTest{$b}{product} <=> $rankedTest{$a}{product}
} keys %rankedTest;
for my $feature ( @features ) {
my $score = $rankedTest{$feature}{product};
print "feature:$feature -> sense:product -> score:$score\n";
}
feature:move -> sense:product -> score:0.309133999169732
feature:dispute -> sense:product -> score:0.0963311089384321
feature:operations -> sense:product -> score:0.0859904478555225
Upvotes: 2