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

Greetings monks,

I have a problem here that I need help with.

My Perl code reads from a file and extracts the required data, storing them in the hierarchy as below. (I used Data::Dumper to print the hash out, and I think it's a hash of arrays of (ref to) hashes, correct?)
#%main_hash -> $keyA -> index[0] -> {$keya => $vala} # -> index[1] -> {$keyb => $valb} # -> index[2] -> {$keyc => $valc} # # -> $keyB -> index[0] -> {$keyd => $vald} # -> index[1] -> {$keye => $vale} # -> index[2] -> {$keyf => $valf}
Then, I would like to compare each value of the hash with that of the previous ones, eg, comparing $valb with $vala, $vale with $vald and so on (comparison only within the same key of main_hash). My Perl code:
# # initializing variables, opening and reading files, etc # # a for loop to parse the file using regex $hash{$key}[$index] = {$key_1 => $val_1}; # end of for loop # writing out the data foreach my $k (sort keys(%main_hash)) { for my ($x = 0; $x < scalar(@{$main_hash{$k}}); $x++) { last if ($x + 1 >= scalar(@{$main_hash{$k}})); my ($first_key, $first_val) = each(%{$main_hash{$k}[$x]}; # take note of the lines below, where I intend to compare # $next_val to $first_val to see which has a larger value, # and if so, output a message saying that it's larger, and so on # my ($next_key, $next_val) = each(%{$main_hash{$k}[$x+1]}); # <== placeholder ==> # followed by some comparison here, eg if next_val > first_val # then do something (you get it) print OUTFILE "$first_key : $first_val\n"; } } # # some other things to do #
Now, the code above works fine, and I'm able to print out the data to the output file just like how I want it to be formatted. However, if I uncomment the line above "# <== placeholder ==>", it gives the "Use of unitialized value in print" errors.

I think I'm missing something here. Is that a pseudo-hash I'm using here? Or did I do anything wrong when dereferencing the hashes?

** Update **
Fixed some parentheses' typos. Thanks to frozenwithjoy for pointing out. I actually typed them out because the actual code is quite cluttered. My apologies.

Replies are listed 'Best First'.
Re: Question about dereferencing multi-dimensional array/hash
by frozenwithjoy (Priest) on Jun 18, 2012 at 05:12 UTC
    You said that the code is working fine as is, but I'm having difficulty understanding how this is possible when you have unmatched open parentheses on the line you are asking about and this line:
    my ($first_key, $first_val) = each(%{$main_hash{$k}[$x]};

    One other thing that stands out to me... The following line lets the loop go one iteration too many. You should use $x instead of $x + 1:

    last if ($x + 1 >= scalar(@{$main_hash{$k}});
    Try changing these and let me know if anything changes.
      Hello, please refer above for the corrected version. Regarding the other thing that you mentioned, nope, I think I need  $x+1, that is because when I reach the last item in the array, I want to exit the inner 'for loop', if not  my ($next_key, $next_val) = each(%{$main_hash{$k}[$x+1]}); would give me 'Uninitialized values' errors.
Re: Question about dereferencing multi-dimensional array/hash
by Athanasius (Archbishop) on Jun 18, 2012 at 07:16 UTC

    Hello jokerzy,

    (I used Data::Dumper to print the hash out, and I think it's a hash of arrays of (ref to) hash, correct?)

    Actually, it’s a hash of refs to anonymous arrays, the elements of which are refs to anonymous hashes — but you’ve got the idea! :-)

    As to the code, frozenwithjoy had the right intuition: the logic of your inner loop is incorrect. However, the fix requires a different approach:

    #! perl use strict; use warnings; use Data::Dumper; my %main_hash = ( keyA => [ { keya => 'vala' }, { keyb => 'valb' }, { keyc => 'valc' }, { keyd => 'vald' }, ], keyB => [ { keye => 'vale' }, { keyf => 'valf' }, { keyg => 'valg' }, { keyh => 'valh' }, ], keyC => [ { keyi => 'vali' }, { keyj => 'valj' }, ], ); print Dumper(%main_hash), "\n"; foreach my $k (sort keys %main_hash) { my @array = @{ $main_hash{$k} }; foreach my $i (1 .. $#array) { my %inner_hash_first = %{ $array[$i - 1] }; my %inner_hash_next = %{ $array[$i ] }; my ($first_key, $first_val) = each( %inner_hash_first ); my ($next_key, $next_val) = each( %inner_hash_next ); print "Now compare ($first_key : $first_val) with ($next_key : + $next_val)\n"; } }

    Note in the above script that dereferencing has been decomposed into separate stages by using intermediate variables (@array, %inner_hash_first, and %inner_hash_next). This is a good idea for two reasons:

    1. It makes the code clearer, and so easier to follow and debug.
    2. In some cases it also promotes efficiency: for example, @{ $main_hash{$k} } is now re-calculated only once for each new value of $k.

    The output from the above script is:

    Now compare (keya : vala) with (keyb : valb) Now compare (keyb : valb) with (keyc : valc) Now compare (keyc : valc) with (keyd : vald) Now compare (keye : vale) with (keyf : valf) Now compare (keyf : valf) with (keyg : valg) Now compare (keyg : valg) with (keyh : valh) Now compare (keyi : vali) with (keyj : valj)

    which shows that each value of each inner hash is available for comparison with its successor (if it has one), as required.

    (You should probably also add a check to verify that each inner hash contains exactly one key/value pair.)

    Update

    Is that a pseudo-hash I'm using here?

    No, according to perldoc, pseudohashes “have been removed from Perl.” See the old thread My views on pseudohashes.

    HTH,

    Athanasius <°(((><contra mundum

      Hi Athanasius, <== My first link to another user :)

      Thanks for the help. I like to way your code is written, especially how they're broken into different sections; it was very easy to grasp. Thanks also for the update about my pseudo-hash question.

      Signing off,
      jokerzy