http://qs1969.pair.com?node_id=11138233

vaitor15 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks Seeking for wisdom I have this hash of hash where I have been asked to find sum of value when cost key is found. In this case 430, I was thinking some how mapping to find all the cost keys and iterating over to sum them. Sample
use Data::Dumper; my $VAR1 = { '153-1' => { '19-4' => { 'cost' => '6300.00', 'cost2' => '630.00' }, '135-1' => { '68-4' => { 'cost' => '300.00', 'cost2' => '130.00' } }, '1069-9' => {}, '35-1' => { '28-4' => { 'cost' => '30.00', 'cost2' => '10.00' } }, }, }; #%result = map { "$_->{0} => $_->{2}" } $VAR1; #print Dumper \%result;
I have tried using map but I am not able for find correct solution any guidance to reach my destination. Thank you in advance wise one!

Replies are listed 'Best First'.
Re: Iterating over hash to find specific key to sum up the cost (updated!)
by haukex (Archbishop) on Oct 29, 2021 at 19:24 UTC

    Final Update: When I replied, the code in the OP did not compile, as one of the four closing braces of $VAR1 was missing, leading me to have to make an assumption of what the data structure looks like. The unmarked update to the OP has now invalidated my code below. Plus, the first nested hash was as shown below instead of the current '19-4' => { 'cost' => '6300.00', 'cost2' => '630.00' }, so the sum of the cost keys was indeed originally 430 as OP expects. vaitor15: It is uncool to update a node in a way that renders replies confusing or meaningless.

    Note what you've posted here is not runnable. See How do I post a question effectively? Update 2: Because it's not runnable, I've had to guess as to what your data structure looks like, but as LanX points out in his reply, other interpretations are possible. Please clarify what your real input data looks like. /Update

    You should read the Perl Data Structures Cookbook (in particular Access and Printing of a HASH OF HASHES) and the Perl References Tutorial to learn how to work with nested data structures like these. Basically, you can use keys to loop over the keys of each level of hashes - this should be enough to get you started:

    use warnings; use strict; my %result = ( "1069-9" => {}, "135-1" => { "68-4" => { cost => "300.00", cost2 => "130.00" } }, "153-1" => { "19-4" => { cost => "100.00", cost2 => "30.00" } }, "35-1" => { "28-4" => { cost => "30.00", cost2 => "10.00" } }, ); for my $k1 ( keys %result ) { for my $k2 ( keys %{ $result{$k1} } ) { if ( exists $result{$k1}{$k2}{cost} ) { print "$k1 / $k2 / $result{$k1}{$k2}{cost}\n"; } } }

    Update: I used exists in the above code to check for the existence of the cost key because you said "find sum of value when cost key is found". I should add that, depending on what values are possible in your input data, you may want to check for definedness or truth instead.

Re: Iterating over hash to find specific key to sum up the cost
by tybalt89 (Monsignor) on Oct 29, 2021 at 20:59 UTC
    #!/usr/bin/perl use strict; use warnings; use List::Util qw( sum0 ); my $hashref = { "153-1" => { "1069-9" => {}, "135-1" => { "68-4" => { cost => "300.00", cost2 => "130.00" } +}, "19-4" => { cost => "6300.00", cost2 => "630.00" }, "35-1" => { "28-4" => { cost => "30.00", cost2 => "10.00" } }, }, }; print costof( $hashref ), " is the total where cost is found\n"; sub costof { my $href = shift; sum0 map /cost/ ? $href->{$_} : costof( $href->{$_} ), keys %$href; }

    Outputs:

    7400 is the total where cost is found
Re: Iterating over hash to find specific key to sum up the cost
by kcott (Archbishop) on Oct 29, 2021 at 23:38 UTC

    G'day vaitor15,

    Welcome to the Monastery.

    [I see from previous replies that you have modified your post one or more times. In the version that I'm seeing, 430 as a total is incorrect; however, other comments indicate that this was correct at one time. It is impossible for us, and therefore you, to tell which solutions are valid. Please ensure that you understand the issues here (by reading "How do I change/delete my post?") and, in any future posts, clearly indicate updates.]

    In my code below, I've laid out the data (as I currently see it) in a manner that makes it more readable and comprehensible. Your current costs (6300+300+30) total 6630.

    #!/usr/bin/env perl use strict; use warnings; my $VAR1 = { '153-1' => { '19-4' => { 'cost' => '6300.00', 'cost2' => '630.00' }, '135-1' => { '68-4' => { 'cost' => '300.00', 'cost2' => '130.00' } }, '1069-9' => { }, '35-1' => { '28-4' => { 'cost' => '30.00', 'cost2' => '10.00' } }, } }; my $total = 0; get_total_cost($VAR1, \$total); printf "Total = %.2f\n", $total; sub get_total_cost { my ($hash_data, $sub_total) = @_; for my $key (keys %$hash_data) { if (ref $hash_data->{$key} eq 'HASH') { next unless keys %{$hash_data->{$key}}; get_total_cost($hash_data->{$key}, $sub_total); } else { if ($key eq 'cost') { $$sub_total += $hash_data->{cost}; } } } return; }

    Note that this doesn't care how deep in the hash the 'cost' key is located. It also ignores 'cost2' keys, and any other variant of a key containing 'cost'; if that's not what you wanted, modify the $key eq 'cost' condition (if it's just keys starting with 'cost', index would be the most efficient; otherwise, a regex would probably be the best solution).

    Output:

    Total = 6630.00

    — Ken

      > In the version that I'm seeing, 430 as a total is incorrect;

      yeah but it's probably the total of

      '153-1' => { '135-1' => { '68-4' => { 'cost' => '300.00', 'cost2' => '130.00'

      let's wait for the next update ... ;)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

Re: Iterating over hash to find specific key to sum up the cost
by LanX (Saint) on Oct 29, 2021 at 19:30 UTC
    your indentation is misleading, the cost entries are at different levels.

    I was thinking of chaining 3 maps but thats not possible here.

    I recommend using Data::Diver or Data::Leaf::Walker or the alternative recursive solutions listed in threads mentioning these modules.

    my $VAR1 = { '153-1' => { '19-4' => { 'cost' => '6300.00', 'cost2' => '630.00' }, '135-1' => { '68-4' => { 'cost' => '300.00', 'cost2' => '130.00' } }, '1069-9' => {}, '35-1' => { '28-4' => { 'cost' => '30.00', 'cost2' => '10.00' } }, }, };

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      your indentation is misleading, the cost entries are at different levels.

      You're right that it's misleading, for my reply I assumed there were two closing braces missing right before '135-1' and '1069-9', in which case the cost keys would be at the same level, but your interpretation is possible as well.

      vaitor15: Please clarify your input data.

      Update: The code in the OP did not compile when I replied because there were missing closing braces. It seems that has now been fixed and you are correct about the nesting.

        anyway I don't understand the requirement "sums up to 430" either.

        FWIW: This old node shows a recursive walker to dive into a randomly nested structure.

        The OP might want to adapt it to his needs.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Iterating over hash to find specific key to sum up the cost
by BillKSmith (Monsignor) on Oct 30, 2021 at 03:06 UTC
    Perhaps you want the sum of all values under a given 'cost key' ('68-4' in this case).
    use strict; use warnings; use List::Util qw(sum); my $VAR1 = { '153-1' => { '19-4' => { 'cost' => '6300.00', 'cost2' => '630.00' }, '135-1' => { '68-4' => { 'cost' => '300.00', 'cost2' => '130.00' } }, '1069-9' => {}, '35-1' => { '28-4' => { 'cost' => '30.00', 'cost2' => '10.00' } }, }, }; my $cost_key = '68-4'; foreach my $temp1 (values %$VAR1) { foreach my $temp2 (values %$temp1) { next if !exists $temp2->{$cost_key}; print sum( values(%{$temp2->{$cost_key}})); } }

    OUTPUT:

    430
    Bill