salamey
salamey

Reputation: 3821

sum hash of hash values using perl

I have a Perl script that parses an Excel file and does the following : It counts for each value in column A, the number of elements it has in column B, the script looks like this :

use strict;
use warnings;
use Spreadsheet::XLSX;
use Data::Dumper;
use List::Util qw( sum );

my $col1 = 0;
my %hash;

my $excel = Spreadsheet::XLSX->new('inout_chartdata_ronald.xlsx');


my $sheet = ${ $excel->{Worksheet} }[0];


$sheet->{MaxRow} ||= $sheet->{MinRow};
my $count = 0;
# Iterate through each row
foreach my $row ( $sheet->{MinRow}+1 .. $sheet->{MaxRow} ) {

# The cell in column 1
my $cell = $sheet->{Cells}[$row][$col1];

if ($cell) {

    # The adjacent cell in column 2
    my $adjacentCell = $sheet->{Cells}[$row][ $col1 + 1 ];  
    # Use a hash of hashes

    $hash{ $cell->{Val} }{ $adjacentCell->{Val} }++;

}
}
print "\n", Dumper \%hash;

The output looks like this :

$VAR1 = {
      '13' => {
                'klm' => 1,
                'hij' => 2,
                'lkm' => 4,
              },
      '12' => {
                'abc' => 2,
                'efg' => 2
              }
    };

This works great, my question is : How can I access the elements of this output $VAR1 in order to do : for value 13, klm + hij = 3 and get a final output like this :

$VAR1 = {
      '13' => {
                'somename' => 3,
                'lkm' => 4,
              },
      '12' => {
                'abc' => 2,
                'efg' => 2
              }
    };

So basically what I want to do is loop through my final hash of hashes and access its specific elements based on a unique key and finally do their sum.

Any help would be appreciated. Thanks

Upvotes: 0

Views: 1188

Answers (2)

David W.
David W.

Reputation: 107090

It sounds like you need to learn about Perl References, and maybe Perl Objects which are just a nice way to deal with references.

As you know, Perl has three basic data-structures:

  • Scalars ($foo)
  • Arrays (@foo)
  • Hashes (%foo)

The problem is that these data structures can only contain scalar data. That is, each element in an array can hold a single value or each key in a hash can hold a single value.

In your case %hash is a Hash where each entry in the hash references another hash. For example:

Your %hash has an entry in it with a key of 13. This doesn't contain a scalar value, but a references to another hash with three keys in it: klm, hij, and lkm. YOu can reference this via this syntax:

${ hash{13} }{klm} = 1
${ hash{13} }{hij} = 2
${ hash{13} }{lkm} = 4

The curly braces may or may not be necessary. However, %{ hash{13} } references that hash contained in $hash{13}, so I can now reference the keys of that hash. You can imagine this getting more complex as you talk about hashes of hashes of arrays of hashes of arrays. Fortunately, Perl includes an easier syntax:

$hash{13}->{klm} = 1
%hash{13}->{hij} = 2
%hash{13}->{lkm} = 4

Read up about hashes and how to manipulate them. After you get comfortable with this, you can start working on learning about Object Oriented Perl which handles references in a safer manner.

Upvotes: 0

choroba
choroba

Reputation: 242443

I used @do_sum to indicate what changes you want to make. The new key is hardcoded in the script. Note that the new key is not created if no key exists in the subhash (the $found flag).

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

use Data::Dumper;

my %hash = (
            '13' => {
                     'klm' => 1,
                     'hij' => 2,
                     'lkm' => 4,
                    },
            '12' => {
                     'abc' => 2,
                     'efg' => 2
                    }
           );
my @do_sum = qw(klm hij);

for my $num (keys %hash) {
    my $found;
    my $sum = 0;
    for my $key (@do_sum) {
        next unless exists $hash{$num}{$key};
        $sum += $hash{$num}{$key};
        delete $hash{$num}{$key};
        $found = 1;
    }
    $hash{$num}{somename} = $sum if $found;
}

print Dumper \%hash;

Upvotes: 1

Related Questions