in reply to Count number of elements in HoH and sum them for other keys

Your specification for the desired result hash is not quite valid: You cannot have a lone array reference in a hash, only  key => value pairs. This can be fixed by giving the array references a key, for example "values". The result hash would then look like this:

( A => { count => 3, B => { count => 2, values => ["n1", "n2"] }, C => { count => 1, values => ["n1" ] } }, D => { count => 3, E => { count => 2, values => ["n2", "n4"] }, F => { count => 1, values => ["n1" ] } } )

It can easily be generated....

...from the HoH that you already have:

You can transform the existing HoH into the specified result hash, using two (nested) loops, and making use of the fact that  scalar @array gives the number of elements in an array:

my %hoh = ( A => { B => [ "n1", "n2" ], C => [ "n1" ] }, D => { E => [ "n2", "n4" ], F => [ "n1" ] }, ); foreach my $col1 (keys %hoh) { my $count1 = 0; foreach my $col2 (keys %{$hoh{$col1}}) { my $count2 = scalar @{$hoh{$col1}{$col2}}; $hoh{$col1}{$col2} = { count => $count2, values => $hoh{$col1}{$col2} }; $count1 += $count2; } $hoh{$col1}{count} = $count1; }

...from the original data:

If you do the counting directly in the code that generates the HoH in the first place, it's even easier - just increment the counters for both levels as you go along:

my %hoh; while (<DATA>) { chomp; my ($c1, $c2, $c3) = split; $hoh{$c1}{count}++; $hoh{$c1}{$c2}{count}++; push @{$hoh{$c1}{$c2}{values}}, $c3; } __DATA__ A B n1 A B n2 A C n1 D E n2 D E n4 D F n1

---
Edit: Refactored the answer to make it more structured.

Replies are listed 'Best First'.
Re^2: Count number of elements in HoH and sum them for other keys
by Sosi (Sexton) on Jun 03, 2014 at 12:55 UTC

    thank you so much. I was a bit confused at the beginning: I thought that you had to specify a starting value for $hoh{$c1}{$c2}{count}. By the way, and I know I'm going a bit astray of the initial question, but what if I wanted to start that count at 5? Would specifying

    $hoh{$c1}{count}=5;

    work if I specified it before incrementing in your while loop? Thanks!

      I thought that you had to specify a starting value for $hoh{$c1}{$c2}{count}.

      When you dereference or modify a non-existing array or hash element, it will automatically "spring to life", including all the necessary intermediate hashes/arrays. For example:

      my %test; $test{a}[2]{b} = 'Hello'; # %test now contains: # ( a => [ undef, # undef, # { b => "Hello" } ] )

      It's called autovivification, and it's one of the nice features that make Perl special... :)   See Wikipedia and perlreftut for more info.

      In addition, the ++ (auto-increment) operator silently treats undef as 0. So you don't need to specify an initial value.


      what if I wanted to start that count at 5?

      One solution would be to create the hash first, and then use another loop to add 5 to each counter.

      Alternatively, you can do a check inside the loop (before incrementing!) to see if the counter has already been incremented previously, and if not, initialize it with the number 5:

      if (!$hoh{$c1}{count}) { $hoh{$c1}{count} = 5; } # verbose form
      $hoh{$c1}{count} ||= 5; # shortcut

      (See C style Logical Or and Assignment Operators.)