Jimmy B Jimmy B - 1 month ago 10
Perl Question

Perl Hash table more than 2 dimensions

I'm trying to create an hash table with 6 dimensions : an hash table of an hash table of an hash table.. etc

I get my data from a file in local (by using REGEX and split function). The data I get are like :

northPartner1;southServiceCall1;northService1;nbNorthCall1;nbSouthCall1


I would like to store the data in many hash tables to use them to create an HTML array.I'm not very comfortable with hash table it's a new concept for me.
The problem is I didn't achieve to create an hash table with many dimensions.

I succeeded to do something like that:

#get the data
my $serviceN;
my $serviceS;
my $partnerN;
my $nbCallN;
my %northServices= () ;
my %northCall= ();

open (TraficFile, "$date/volumetries.csv") || die "Can't open: $!";
while(defined($_=<TraficFile>)) {
my @champsOutput = split(/\;/);
#get services.
if ($champsOutput[4]=~/Service Call/i){next;}
if ($champsOutput[4]=~/.*@/) {
($serviceS, $serviceN) = split(/@/,$champsOutput[4]);
$partnerN = $champsOutput[0];

}#endif
$nbCallN = $champsOutput[5];
print "nb : $nbCallN \n";

$northServices{$serviceN}{$partnerN}++;
}#while
#Print result
foreach $serviceN (keys %northServices){
print ('***'," $serviceN ",'***',"\n");
foreach my $partnerN ( keys %{ $northServices{$serviceN} } ){
print " $partnerN \n";
}
}


I have for each North Service the name of the North Partners,
but I would like to add hash tables like that :

$northServices={

'nameNorthService1' => {
'nameNorthPartner1' => 'numberOfNorthCall' => 'nameSouthService1' => 'numberOfCall',
=> 'nameSouthService2' => 'numberOfCall',
=> 'nameSouthService3' => 'numberOfCall',

'nameNorthPartner2' => 'numberOfNorthCall' => 'nameSouthService1' => 'numberOfCall',
=> 'nameSouthService2' => 'numberOfCall',
}
'nameNorthService2' => {
'nameNorthPartner1' => 'numberOfNorthCall' => 'nameSouthService1' => 'numberOfCall',
}
...
}


The
numberOfNorthCall
is divided in many South services which have each an
numberOfCall
.

I really don't know how to create others hash tables to encapsulate all this data like the model above.
I tried to create an other hash table in the
while()
, for example :

$northCall{$nbCallN} = $nbCallN;
$serviceNord{$serviceN}{$partnerN}{$northCall{$nbCallN}}++;


I tried to create references too :

my $rec = {};
$serviceNord{$serviceN}{$partnerN} = $rec;
$rec->{$nbCallN}++;


It seem to be the wrong way, but I didn't find anything to help me to create hash table with more than two dimensions.

If you know a way, do not hesitate.

Answer

First off - I'm going to suggest you're trying to do something entirely too complicated. Whilst you can do a hash of hashes of hashes etc. this is going to be quite painful. However the root of your problem I think will be because you're misunderstanding the difference between: -> and =>. The former dereferences a hash. The latter is functionally equivalent to a comma.

But pretty fundamentally - you don't have multidimensional hashes - you have a top level hash of hash references.

If you do:

use strict;
use warnings;

my %hash;

$hash{firstlayer}{secondlayer}++;
print $hash{firstlayer};

You'll get a hash reference.

However, I think what you might want to consider is stopping using complex nested hashes, and instead try and put together an object. (Don't get put off - objects are basically 'complex data structures' that might include some code).

To create a nested hash declaratively:

use strict;
use warnings;
use Data::Dumper;

my $value = "value";

my $services = {
    key1 => $value,
    key2 => {
        subkey  => $value,
        subkey2 => $value,
    },
    key3 => {
        subkey1 => {
            subsubkey1 => $value,
            subsubkey2 => $value,
        },
    },
};

print Dumper \$services;

You can also add/access via:

   print $services->{key3}->{subkey1}->{subsubkey2};

If you had created it with a %sigil, e.g.

my %services = (
#NOTE - different bracket too!

then that'd be

print $services{key3}{subkey1}{subsubkey2};

They're functionally similar - the key difference is that '->' tells perl that this is a reference to follow. Which style you use is more a matter of what makes more sense in your code.

But I would still suggest consider doing it OO style, and create an object with properties. (Objects are basically hashes, but let you do some more interesting things)

#MyService.pm

use strict;
use warnings;

package MyService;

sub new {
    my ( $class ) = @_;
    my $self = {};
    bless ( $self, $class ); 
    return $self;
}

sub set_name { 
    my ( $self, $name ) = @_;
    $self -> {name} = $name;
}

sub add_partner { 
    my ( $self, $partner ) = @_;
    push ( @{ $self -> {partners} }, $partner ); 
}

sub get_partners {
    my ( $self ) = @_;
    return @{ $self -> {partners} };
}

And then you can:

use strict;
use warnings;

use MyService;

my @services;
my $service = MyService -> new();
push ( @services, $service );

$service -> set_name ( 'nameNorthService1' );