I have the following hashed structure

$chainStorage{$R1}{$S1}{$C1}{@A1}

$chainStorage = {
'ACB' => {
'E' => '06' => [100, 200, 95]
'B' => '23' => [20, 1000, 05, 30]
},
'AFG' => {
'C' => '24' => [18, 23, 2300, 3456]
},
'HJK' => {
'A' => '12' => [24, 25, 3200, 5668]
'D' => '15' => [168]
}
};

For example, ACB corresponds to two arrays,[100, 200, 95] and [20, 1000, 05, 30];
while ‘E’ corresponds to [100, 200, 95] only.

Right now, I need to add all of the elements in the array corresponding to the first-level key, e.g., ‘ACB’, together.

In other words, in another hash structure, I want ACB corresponds to
100+200+95 +20+1000+05+30 = 1450

How to implement this functionality over $chainStorage

Dani AI

Generated

’s intent looks like a top-level key mapping down to arrayrefs (three hash levels then an arrayref). was right to flag the original literal as ambiguous, and ’s nested-loop approach is a straightforward solution. Below is a defensive, reusable subroutine and a compact alternative that avoid repeating the exact examples shown in the replies.

use strict;
use warnings;
use List::Util qw(sum);

sub sum_by_top_key {
    my ($chain) = @_;
    my %sums;

    while (my ($top, $second_hr) = each %{$chain}) {
        my @nums;
        for my $second_val (values %{$second_hr}) {
            next unless ref $second_val eq 'HASH';
            for my $arr_ref (values %{$second_val}) {
                push @nums, @$arr_ref if ref $arr_ref eq 'ARRAY';
            }
        }
        my $total = sum(@nums);
        $sums{$top} = defined $total ? $total : 0;
    }

    return \%sums;
}

# Usage:
# my $totals = sum_by_top_key($chainStorage);

For a terser approach (same logic in a map), a one-liner can be used but is harder to read; the sub above is safer for real code.

Notes and pitfalls: enable use warnings/use strict and verify the actual data shape at runtime (hashrefs vs arrayrefs). Beware numeric literals with leading zeros in Perl source — e.g. 05 is an octal literal — so prefer strings or explicit numeric conversion when values come from external sources. If the tertiary hash always contains a single key, consider normalizing the data (remove that extra hash layer) to simplify iteration. If nesting depth may vary, replace the inner loops with a small recursive walker that collects all arrayrefs before summing.

Recommended Answers

All 2 Replies

Use code tags instead of quote tags to preserve formatting.

The code you've posted doesn't describe the structure you've said it does. => is just syntactic sugar for a comma, so $chainStorage->{ACB} is

{
    E => '06',
    [100, 200, 95] => 'B',
    '23' => [20, 1000, 5, 30],
}

Assuming you meant the following (formatting added)...

$chainStorage = {
    ACB => {
        E => { '06' => [100, 200, 95] },
        B => { '23' => [20, 1000, 5, 30] },
    },
    AFG => {
        C => { '24' => [18, 23, 2300, 3456] },
    },
    HJK => {
        A => { '12' => [24, 25, 3200, 5668] },
        D => { '15' => [168] },
    },
};

... then you still have kind of an odd situation because your tertiary (third-layer) hashes never have more than one key and one value. If that's always the case it'd probably be better to think of a different way to store them. You might, for instance, make the tertiary key the first element of the list, like this:

ACB => {
        E => ['06', 100, 200, 95],
        B => ['23', 20, 1000, 5, 30],
    },

Then use something like this to add the elements:

my $href = $chainStorage->{ACB};

my $sum = 0;
foreach my $aref (values %$href) {
        my @ary = @$aref;
        @ary = @ary[1..$#ary]; # I had to slice the first element back out
        $sum += $_ foreach (@ary);
}

If you can't restructure your data like this, it'll be an extra step to pull the array out of the interior hashref, but that shouldn't be too much of a stretch... Without making unwarranted assumptions about your data structure, I can't give you any more, but maybe this is enough to go on.

Building on Trentacle's first correction of your data structure you could try the following:

#!/usr/bin/perl;
use strict;
use warnings;
use Data::Dumper;

my $chainStorage = {
    ACB => {
        E => { '06' => [100, 200, 95] },
        B => { '23' => [20, 1000, 5, 30] },
    },
    AFG => {
        C => { '24' => [18, 23, 2300, 3456] },
    },
    HJK => {
        A => { '12' => [24, 25, 3200, 5668] },
        D => { '15' => [168] },
    },
};

my %results;

foreach my $flk(keys %$chainStorage){
    $results{$flk} = 0;
    
    foreach my $slk(keys $$chainStorage{$flk}){
        foreach my $tlk(keys $$chainStorage{$flk}{$slk}){
            foreach my $aref ($$chainStorage{$flk}{$slk}{$tlk}){
                foreach my $elem(@$aref){
                    $results{$flk} += $elem;
                }
            }
        }
    }
}

print Dumper(\%results);

Outputs

$VAR1 = {
          'AFG' => 5797,
          'HJK' => 9085,
          'ACB' => 1450
        };
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.