Question

I am trying to generate a hash using hashes of hashes with array values concept. I am not sure if my syntax is right.

Below is the code section that I am hoping should create hashes of hashes with array values.

 use strict;
 %hash=();
 open IN, "samplefile.txt" or die "cannot open file:$!"; 
 while(<IN>){
     chomp $_;
     my @split=split("\t", $_);
     $hash{$split[0]}{$split[1]}=push @{ $hash{$split[0]}{$split[1]} },$split[2];
     push(@array, $split[1]);
  }

Sample dataset:

4 10 2
9 4 3
4 3 2
4 3 8
4 10 5
4 5 2

Expected hash.

%hash=(
    '4'=> {
    '10'=>'2, 5'
     '5' => '2'
     '3' => '2,8'
   }
 '9'=>{
     '4'=>'3'
 }
)
Was it helpful?

Solution

I think you actually want

%hash = (
   '4' => {
      '10' => [ 2, 5 ],
      '5'  => [ 2 ],
      '3'  => [ 2, 8 ],
    },
    '9' => {
       '4' => [ 3 ],
    },
);

Solution:

my %hash;
while (<>) {
   my @F = split;
   push @{ $hash{ $F[0] }{ $F[1] } }, $F[2];
}

Thanks to autovivification, that will automatically create the hashes and arrays as needed.

You can always use join ',' afterwards if you really do want strings instead of arrays.

for my $k1 (keys(%hash)) {
   for my $k2 (keys(${ $hash{$k1} })) {
      $hash{$k1}{$k2} = join(',', @{ $hash{$k1}{$k2} });
   }
}

OTHER TIPS

If you're using push, you shouldn't be using an equal sign (unless you want the number of items pushed). That tells me there's an issue there. You're probably getting a bit confused about your structure. You either are pushing data into your array reference, or you meant to join the items and push it into another structure. What you've written is a bit hard to understand because the syntax can be so difficult to understand.

It looks like you want to play around with references and see if you can get the hang of them. When you're dealing with complex structures like this, it's easier to understand if you have some ...uh... real world data. Let's create a real world hash of a hash of arrays and that will be a bit easier to track.

Let's make a hash containing people. The hash will contain the data and will be keyed by the person's name. We'll call this %people. We will have two people in our hash, Bob, and Janet. Bob and Janet have various addresses and phone numbers. We will have a reference to a hash that will contain the names of our data fields called PHONE and ADDRESS.

$people{Bob}->{ADDRESS};    # Bob's addresses
$people{Bob}->{PHONE};      # Bob's phone numbers
$people{Janet}->{ADDRESS};  # Janet's Addresses
$people{Janet}->{PHONE};    # Janet's Phone numbers

Note the arrow syntax. This tells you that $people{Bob} contains a reference to an other hash and not mere scalar data.

Next, Bob and Janet could have multiple addresses and phone numbers so instead of storing one phone number, we'll have $people{Bob}->{PHONE} store a reference to an array of phone numbers while $people{Bob}->{ADDRESS} will contain a reference to an array of addresses.

Here's a short program that reads in the data. Each time a line starts with person:, I assume that the following data will be for that person. A person could have addresses and phone numbers. I'll store them as arrays under the correct hash.

As ikegami stated, Perl has what is called auto vivification which let's Perl figure out exactly what type of reference you're pointing to. However, I like stating what I expect the data structure will hold for documentation purposes. The following statements in my program are not really necessary, but I find useful:

$people{$person} = {};  # This is a reference to another array

and

if ( not exists $people{$person}->{ADDRESS} ) {
    $people{$person}->{ADDRESS} = [];  #This is a reference to an array
}

Here's my program:

#! /usr/bin/env perl
#
use strict;
use warnings;
use Data::Dumper;
use feature qw(say);

my %people;
my $person;
while ( my $line = <DATA> ) {
    chomp $line;
    my ( $type, $data ) = split /:\s*/, $line;
    if ( $type eq "person" ) {
        $person = $data;
        $people{$person} = {};  # This is a reference to another array
    }
    if ( $type eq "address" ) {
        if ( not exists $people{$person}->{ADDRESS} ) {
            $people{$person}->{ADDRESS} = [];  #This is a reference to an array
        }
        push @{ $people{$person}->{ADDRESS} }, $data;
    }
    if ( $type eq  "phone" ) {
        if ( not exists $people{$person}->{PHONE} ) {
            $people{$person}->{PHONE} = [];    #This is a reference to an array
        }
        push @{ $people{$person}->{PHONE} }, $data;
    }
}
say Dumper \%people;
__DATA__
person: Bob
address: 120 Main Street, Suburbville CO
address: 123 Wage Slave Lane, Industry CO
address: 1 Beach Hut Road, Getaway CA
phone: 555-1111
phone: 555-2222
person: Janet
address: 230 Oak Tree Road, Gloomsburg CO
address: 544 Initech Road, Businesstown CO
phone: 555-3333
phone: 555-4444

This produces:

$VAR1 = {
        'Bob' => {
                    'PHONE' => [
                                '555-1111',
                                '555-2222'
                                ],
                    'ADDRESS' => [
                                    '120 Main Street, Suburbville CO',
                                    '123 Wage Slave Lane, Industry CO',
                                    '1 Beach Hut Road, Getaway CA'
                                ]
                },
        'Janet' => {
                    'PHONE' => [
                                    '555-3333',
                                    '555-4444'
                                ],
                    'ADDRESS' => [
                                    '230 Oak Tree Road, Gloomsburg CO',
                                    '544 Initech Road, Businesstown CO'
                                    ]
                    }
        };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top