Search code examples
perlhashmapperl-data-structures

Hash reference - Reuse inner hash to store and retrieve next values


I am new to Perl programming and have to work on Hash of Hashes. I am trying to reuse innerHash variables to be dynamic in nature. And I expect the inner items to be stored in outerHash how ever many I have added. Below is my test program snippet.

use warnings;
sub testHash {  
  my %outerHash = ();

  my %innerHash1 = ();
  my %innerHash2 = ();

  $innerHash1{"key1"} = "value1";
  $innerHash1{"key2"} = "value2";
  $innerHash1{"key3"} = "value3";
  $outerHash{"Master1"} = \%innerHash1;

  $innerHash2{"key4"} = "value4";
  $innerHash2{"key5"} = "value5";
  $innerHash2{"key6"} = "value6";
  $outerHash{"Master2"} = \%innerHash2;

  #delete $innerHash1{$_};
  %innerHash1 = ();
  #undef %innerHash1;

  $innerHash1{"key7"} = "value7";
  $innerHash1{"key8"} = "value8";
  $innerHash1{"key9"} = "value9";
  $outerHash{"Master3"} = \%innerHash1;

  foreach $outerItem (keys %outerHash){
    print "\n$outerItem: ";
    foreach $innerItem (keys %{$outerHash{$outerItem}}){
        print "\t $innerItem = $outerHash{$outerItem}{$innerItem}";
    }
    print "\n-------------------------------------------------------";
  }
  print "\n";
}

testHash;

Output:

Master3:     key8 = value8   key7 = value7   key9 = value9
-----------------------------------------------------------------
Master2:     key5 = value5   key6 = value6   key4 = value4
-----------------------------------------------------------------
Master1:     key8 = value8   key7 = value7   key9 = value9
-----------------------------------------------------------------

I understand it's taking the newer reference of innerHash1 while printing the items. What is the right way to have all the right elements in outerHash? In a real programming scenario I cannot declare n variables in advance.


Solution

  • You may be new to perl, but you understand the concept of pointers don't you? The code $outerHash{"Master1"} = \%innerHash1; means that "$outerHash{"Master1"} is assigned a pointer to %innerHash1". So what you're observing is correct and expected.

    If you want to keep recreating %innerHash and adding it to %outerHash, you will need to do one of two things:

    • Assign by value. $outerHash{"Master1"} = {}; %{$outerHash{"Master1"}} = %innerHash1;
    • Only add %innerHash to %outerHash once per loop. Since %innerHash is redeclared within the loop, not outside of it, it goes out of scope every loop, but its contents will be kept in outerHash due to perl's reference counting. Depending on your perspective, this could be considered "trying to be too clever and potentially dangerous", but I think it's fine.