Merging of duplicate path values of a Perl HOH

Viewed 65

I have a perl HOH (hash of hash) wherein i have serial numbers 1,2,3.... so on and directory paths with associated counter values. Could anyone please suggest a way in which i can merge the duplicate internal paths (e.g, /usr/lib , /bin/ ) to a form a new hash with addition of the values?

Sample HOH:

$VAR1 = {
          '1' => {
                   '/usr/lib' => 18
                 },
          '3' => {
                   '/bin/' => '3'
                 },
          '4' => {
                   '/usr/lib' => 12
                 },
          '2' => {
                   '/bin/' => '6'
                 },
          '5' => {
                   '/dev/' => '2'
                 },
          '6' => {
                   '/tmp/' => '8'
                 }
        };

Final output requirement i am looking for is a simple hash with combined values. No need of serial numbers:

$VAR1 = {
              '/usr/lib' => '30',
              '/bin/' => '9',
              '/dev/' => '2',
              '/tmp/' => '8'
              
        };
3 Answers

Try

sub aggregate_counts {
    my $HOH = shift;
    my %out;
    for my $h (values %$HOH) {
        for my $k ( keys %$h) {
            $out{$k} += $h->{$k};
        }
    }
    \%out
}

Use this subroutine like aggregate_counts($hoh) to get hash reference in required format.

Complete Script for testing:

use strict;
use warnings;
sub aggregate_counts {
    my $HOH = shift;
    my %out;
    for my $h (values %$HOH) {
        for my $k ( keys %$h) {
            $out{$k} += $h->{$k};
        }
    }
    \%out
}

my
$VAR1 = {
          '1' => {
                   '/usr/lib' => 18
                 },
          '3' => {
                   '/bin/' => '3'
                 },
          '4' => {
                   '/usr/lib' => 12
                 },
          '2' => {
                   '/bin/' => '6'
                 },
          '5' => {
                   '/dev/' => '2'
                 },
          '6' => {
                   '/tmp/' => '8'
                 }
        };


use Data::Dumper;

print Dumper(aggregate_counts($VAR1))

Output:

$VAR1 = {
          '/dev/' => '2',
          '/tmp/' => '8',
          '/bin/' => 9,
          '/usr/lib' => 30
        };

This could help you:

use strict; use warnings;
use Data::Dumper;

my %hash = (
'1' => {
       '/usr/lib' => 18
     },
'3' => {
       '/bin/' => '3'
     },
'4' => {
       '/usr/lib' => 12
     },
'2' => {
       '/bin/' => '6'
     },
'5' => {
       '/dev/' => '2'
     },
'6' => {
       '/tmp/' => '8'
     }
);

my %result;

foreach my $key (keys %hash){
    foreach my $inner (keys %{$hash{$key}}) {
        $result{$inner} += $hash{$key}{$inner};
    }
}

print Dumper(\%result);

Output:

$VAR1 = {
          '/usr/lib' => 30,
          '/bin/' => 9,
          '/dev/' => 2,
          '/tmp/' => 8
        };

Note: Please post your code as well in the question while asking for a help in SO.

You don't care about the keys of the outer hash. So let's start with values(%$VAR1):

{ '/usr/lib' => 18 },
{ '/bin/' => '3' },
{ '/usr/lib' => 12 },
{ '/bin/' => '6' },
{ '/dev/' => '2' },
{ '/tmp/' => '8' },

Hashes are great for grouping. We're going to iterate over the above list, then we're going to iterates over the elements of each of those hashes, using a hash to group them.

my %grouped;
for my $inner (values(%$VAR1)) {
   for my $key (keys(%$inner)) {
      my $val = $inner->{$key};
      $grouped{$key} += $val;
   }
}

And we're already done.

Related