-1

I have a hash of hashes where the values are all numerical. I can sort fine using the sort command and or to sort the hash values in order first to last, but what if I want to weight the results instead of it just being in order of keys specified? Is there a way to do that?

EDIT: Ok, here's the code...

my @check_order = ["disk_usage","num_dbs","qps_avg"];
my %weights     = ( disk_usage => .7,
                    num_dbs    => .4,
                    qps_avg    => .2
);
my @dbs=sort { 
    ($stats{$a}->{$check_order[0]}*$weights{$check_order[0]}) <=>
    ($stats{$b}->{$check_order[0]}*$weights{$check_order[0]})  or
    ($stats{$a}->{$check_order[1]}*$weights{$check_order[1]}) <=> 
    ($stats{$b}->{$check_order[1]}*$weights{$check_order[1]}) or
    ($stats{$a}->{$check_order[2]}*$weights{$check_order[2]}) <=> 
    ($stats{$b}->{$check_order[2]}*$weights{$check_order[2]})
} keys(%stats);
Jean
  • 21,665
  • 24
  • 69
  • 119
user578086
  • 321
  • 1
  • 4
  • 12
  • 4
    You are using `[ ... ]` to assign to your array, which is wrong in this case. The angled brackets create an anonymous array ref, which is a single scalar value contained in the first element. `use Data::Dumper; print Dumper \@check_order;` and you may (or may not) realize your mistake. Use parentheses when assigning to arrays. – TLP Jul 01 '13 at 14:39
  • Ah thanks that's true, but the weights still don't work. Any idea why? Hmm... – user578086 Jul 01 '13 at 14:45
  • what is your expected result ? – Jean Jul 01 '13 at 14:45
  • That the weight would affect preference of value in the sort as a alternative to the order. Is that possible? – user578086 Jul 01 '13 at 14:49
  • 1
    @user578086 You are under the impression that we are omniscient. We are not. If you want us to explain to you why your code does not do what you expect with your input, you need to show us your code and your input. Create a [Short, Self-Contained, Correct Example](http://sscce.org/) that demonstrates your problem. – TLP Jul 01 '13 at 14:52
  • Well I have a hash of hashes like %hash=( 'item1' => { data1=12, data2 = 13 ...}, 'item2' => { data1=14, data2=2 ...} ...) and my goal is to sort the main keys (item1, item2, etc) based on the values of the keys they contain. The current sort seems to achieve that, but only based on the order of the sort. What I'm wondering is if it's now possible to say the values of data2 are more important then data1 via weight values instead of having to rely on order of operation. Hopefully that clears things up a bit. :) – user578086 Jul 01 '13 at 14:59
  • 2
    You realize that you're not weighting *anything*, here? If you multiply both sides of an equation by the same factor, you get the same verdict as before you multiplied. – Axeman Jul 01 '13 at 15:03
  • So do I only multiply one side then? – user578086 Jul 01 '13 at 15:06
  • Well, even if done only on one side of each sort the weight still does not change the preference of one item over the other... – user578086 Jul 01 '13 at 15:12
  • So is this not possible to accomplish? – user578086 Jul 01 '13 at 15:25
  • Of course it's possible. But the way your question was asked, it's impossible to answer. Show us some sample data and the output you'read trying to achieve. – innaM Jul 01 '13 at 16:40

2 Answers2

1

You want to sort the list based on a function value of each element. So use a function in your sort statement.

@sorted = sub { sort_function($a) <=> sort_function($b) } @unsorted;

sub sort_function {
    my ($input) = @_;

    return $input->{disk_usage} * 0.7 
         + $input->{num_dbs} * 0.4 
         + $input->{qps_avg} * 0.2;

    # -or- more generally

    my $value = 0;
    while (my ($key,$weight) = each %weights) {
        $value += $input->{$key} * $weight;
    }
    return $value;
}

When your sort function is expensive and there are many items to be sorted, a Schwartzian transform can improve the performance of your sort:

@sorted = map { $_->[0] }
          sort { $a->[1] <=> $b->[1] }
          map { [ $_, sort_function($_) ] } 
          @unsorted;
Community
  • 1
  • 1
mob
  • 117,087
  • 18
  • 149
  • 283
0

If your weights are stored in another hash %property This will sort hash keys based on the product $hash{key} * $property{key}

#!/usr/bin/perl
use strict;
use warnings;

my %hash = (
  a => 51,
  b => 61,
  c => 71,
);

my %property = ( a => 7, b => 6, c => 5 );


foreach (sort { ($hash{$a}*$property{$a}) <=> 
                ($hash{$b}*$property{$b}) } keys %hash)
{
    printf("[%d][%d][%d]\n",
    $hash{$_},$property{$_},$hash{$_}*$property{$_});
}
Jean
  • 21,665
  • 24
  • 69
  • 119
  • Thanks for the suggestion, however I made edits accordingly as shown below but the weight values don't seem to change the results at all. Any idea why? Thanks. – user578086 Jul 01 '13 at 14:31
  • my @check_order=["disk_usage","num_dbs","qps_avg"]; my %weights=(disk_usage=>.7,num_dbs=>.4,qps_avg=>.2); my @dbs=sort { ($stats{$a}-> {$check_order[0]}*$weights{$check_order[0]}) <=> ($stats{$b}->{$check_order[0]}*$weights{$check_order[0]}) or ($stats{$a}->{$check_order[1]}*$weights{$check_order[1]}) <=> ($stats{$b}->{$check_order[1]}*$weights{$check_order[1]}) or ($stats{$a}->{$check_order[2]}*$weights{$check_order[2]}) <=> ($stats{$b}->{$check_order[2]}*$weights{$check_order[2]})} keys(%stats); – user578086 Jul 01 '13 at 14:33
  • 3
    @user578086 Don't paste code into the comments, add it to your question instead, properly formatted – TLP Jul 01 '13 at 14:35