4

I have a stored multidimensional hash (%info) with the following structure:

$info{$os}{$id}=$length;

foreach $os (keys %info){   
    foreach $id (keys %{$info{$os}}){
        print "$os $id => " . $info{$os}{$id} ."\n" if (keys %info > 100);
    }
}

With this, I can read the hash and print only those $os with more than 100 occurrences, but now I would like to print only the $id with highest $length (i.e., values). So I would like to sort the hash by values and print only $os and $id with highest value.

brian d foy
  • 129,424
  • 31
  • 207
  • 592

3 Answers3

4

Can use List::Util::reduce to get the key with the largest value, within each top-level key

use List::Util qw(reduce);

for my $os (keys %info) { 
    my $id_max_length = reduce { 
        $info{$os}{$a} > $info{$os}{$b} ? $a : $b 
    } keys %{$info{$os}};

    say "$os --> $id_max_length --> $info{$os}{$id_max_length}";
}

To get the highest value among all keys

my ($max_os, $max_id) = 
    map { $_->[0], $_->[1] }
    reduce { $info{$a->[0]}{$a->[1]} > $info{$b->[0]}{$b->[1]} ? $a : $b }
    map { [$_, max_id_for_os($_)] } 
    keys %info;

say "$max_os -> $max_id -> $info{$max_os}{$max_id}";

sub max_id_for_os {
    my ($os) = @_; 
    reduce { $info{$os}{$a} > $info{$os}{$b} ? $a : $b } keys %{$info{$os}}
}

Or, perhaps simpler, compare in the loop over top-level keys

my ($max_os, $max_id) = do {  # initialize
    my ($os) = keys %info;
    $os, (keys %{$info{$os}})[0];
};

for my $os (keys %info) { 
    my $mid = 
        reduce { $info{$os}{$a} > $info{$os}{$b} ? $a : $b } 
        keys %{$info{$os}};

    ($max_os, $max_id) = ($os, $mid) 
        if $info{$os}{$mid} > $info{$max_os}{$max_id};
}

say "Largest: $max_os -> $max_id -> $info{$max_os}{$max_id}";
zdim
  • 64,580
  • 5
  • 52
  • 81
2
my($maxos,$maxid,$maxlength);
foreach my $os (sort keys %info) {
  foreach my $id (keys %{ $info{$os} }) {
    ($maxos,$maxid,$maxlength) = ($os,$id,$info{$os}{$id})
      if !defined $maxlength || $info{$os}{$id} > $maxlength;
  }
}

print "$maxos $maxid => $maxlength\n";
Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
  • This is a good answer if OP does not actually need to sort. (He says that he needs to sort but it is reasonable to surmise that he might not need that.) – thb Nov 24 '19 at 00:47
1

Sometimes the hardest thing to do is precisely to define the problem. Try this code:

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

my %info = (
    Debian  => { jessie => 80, buster => 90, bullseye => 110 },
    Fedora  => { fedora_a => 70, fedora_b => 105 },
    Arch    => { arch_a => 50, arch_b => 108 },
    Windows => { vista => 40, win10 => 63 },
    OSX     => { apple => 71 },
);

my @key_pairs_unsorted;
for my $os (keys %info) {
    for my $id (keys %{$info{$os}}) {
        push @key_pairs_unsorted, {os => $os, id => $id};
    }
}
my @key_pairs_sorted =
  sort { $info{$b->{os}}{$b->{id}} <=> $info{$a->{os}}{$a->{id}} }
  @key_pairs_unsorted;

printf "%-7s %-8s %3d\n",
  $_->{os}, $_->{id}, ${info}{$_->{os}}{$_->{id}}
  for @key_pairs_sorted;

Understanding this code should help you precisely to define your problem, after which you should be able to solve the problem reasonably swiftly.

For further reference, this question or this one might be of interest.

thb
  • 13,796
  • 3
  • 40
  • 68