Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Need help with three-dimensional hash 1

Status
Not open for further replies.
Aug 19, 2002
10
0
0
DK
Hi,

I'm trying to create a three-dimensional hash but I can't seem to get it right. In the code beneath I push data into my Hash of Hash of Hash = $HoHoH. This seems to work fine.

foreach my $line (@aonly){
if($line =~ m/^(.*)("\/)(\d+)(\/)(.*)(\/)(.*)(\/)(.*)(")/){
# do away with double lines
if($data[$id-1] eq "$3 $9"){
}else{
$id = push @data,"$3 $9";
push @{$HoHoH{$3}->{$5}}, $9;
}
}
}


Now next I'm trying to loop over the three-dimensional hash and return all values inside.

for my $custnumber (keys %HoHoH){
print "$custnumber\n";
for $nodename (keys %{ $HoHoH{$custnumber} } ){
print "$nodename\n";
for $filename (keys %{$HoHoH{$custnumber}{$nodename}}){
print "$filename\n";
}
}
print "\n\n";
}

This is the part I can't get to work. When I run the script it tells me "Can't coerce array into hash at test.pl line 69" which referres to this line "for $filename (keys %{$HoHoH{$custnumber}{$nodename}}){".

Can anyone please help me understand how to work with this kind of data structure.

Best regards
DiXiE
 
You say you have a hash of hashes of hashes, but your push @{$HoHoH{$3}->{$5}}, $9; statement means you actually have a hash of hashes of arrays. Try
Code:
use Data::Dumper;
$Data::Dumper::Indent = 3;

# and after you build your structure

print Dumper(%HoHoH);
which will give you a pretty-print of the structure so you can see what you have. You need to set hashes to a value, you can't use push.
 
Firstly, your structure:
Code:
if ($data[$id-1] eq "$3 $9"){
} else {
would be far better expressed as either:
Code:
if ($data[$id-1] ne "$3 $9"){
or:
Code:
unless ($data[$id-1] eq "$3 $9"){

Secondly, your data retrieval code uses for instead of foreach, which is the correct form for the code you've written, in all three loops.

Finally, your given problem, the line:
Code:
for $filename (keys %{$HoHoH{$custnumber}{$nodename}}){
doesn't work, firstly because the second hash is referential and secondly because the code you've written pushes data to an array in the third dimension, not to another hash. You need to use:
Code:
for[b]each[/b] $filename ([b]@[/b]{$HoHoH{$custnumber}[b]->[/b]{$nodename}}){
to iterate the data you're storing.
 
Hi MOrac,

Makes sence but I would like to use a hash of hash of hash so it seems I have not understod how to make a three-dimensional hash. I would appreciate if you could answer these two questions for me.

How would I go about doing that and how would I loop through it to retrive my data?

Best regards
DiXiE
 
Well, you don't actually need a three-dimensional hash as you only have three degrees of data and not four. But if you want to insert an irrelevant fourth degree in order to use only hash keys, then the insert routine would need to become something like:
Code:
$HoHoH{$3}->{$5}->{$9} = 1;
at which point the retrieval code would revert back to your original format:
Code:
foreach $filename (keys %{$HoHoH{$custnumber}->{$nodename}}) {
 
MOrac,

Secondly, your data retrieval code uses for instead of foreach, which is the correct form for the code you've written, in all three loops.

And where did you get this idea from? Have a look at Foreach Loops in 'perldoc perlsyn'.

From which I quote:

"The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity."

Barbie
Leader of Birmingham Perl Mongers
 
Well, you don't actually need a three-dimensional hash, as you only have three degrees of data, and not four. But if you want to insert an irrelevant fourth degree in order to use only hash keys, then the insert routine would need to become something like:
Code:
$HoHoH{$3}->{$5}->{$9} = 1;
at which point the retrieval code would revert back to your original format:
Code:
foreach $filename (keys %{$HoHoH{$custnumber}->{$nodename}}) {
 
From which I quote:

"The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity."

Readability. Ah, yes, there it was. Syntactically they are very different statements, and every perl manual defines the construct as foreach, not for. The synonym exists because the Bourne shell script for statement works in this second fashion, and perl loves to adopt. The most common usage of for is still the C language format, which is why that form has no other name than for, whereas foreach is used to be able to differentiate the lesser-used for form. If you're not a Bourne shell purist, use the correct name, which is foreach. It's like English grammar - just because common usage, especially in the US, allows for poor grammar it doesn't mean that it's a good thing to do. And it's never clever.
 
Hi MOrac,

Thank U. U have been most helpful, as to the discussion of english grammer I really don't know, but I do take notice of your ending point that americans arn't clever ;-)

As for my question I realized that the populating structure was in fact what I needed and adapting your code
Code:
foreach $filename (keys %{$HoHoH{$custnumber}->{$nodename}}) {
made my rutine work like a charme. A star to U!
 
.. and every perl manual

I quoted from the definitive Perl manual, 'perldoc perlsyn', so quite clearly not every perl manual states it. Please bear in mind we are talking Perl here, not C, and not Bourne shell. Regardless of how you are used to doing it in other languages, this is Perl. The correct name is 'foreach' or 'for' whichever you prefer. It has nothing to do with common usage or English grammar, they are both correct.

Syntactically they are very different statements

No, not 'are', but 'can be'.

The synonym exists because Larry likes the idea of being able to read Perl code as a spoken language. He's a linguist, so thats not too surprising :)

Barbie
Leader of Birmingham Perl Mongers
 
Last post for me on this as an off-subject follow-up - Barbie, it would be helpful if you actually read the manual you quote. The heading of the section is 'Foreach Loops'. Not 'For or Foreach Loops'. And, as I said, every Perl manual reads this way.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top