libxelar.so
libxelar.so

Reputation: 543

Inserting and iterating over hash of arrays

I have following input data

Country1:operator1
Country1:operator2
Country1:operator3

Country2:operator1
Country2:operator2
Country2:operator3

I would like to insert this data into hash %INFO such that every key corresponds to an array of "operators", so I could iterate in following way

foreach $i ( keys %INFO ) {

    foreach $operator ( $INFO{$i} ) {
        print " $i ---> $operator \n";  
    }
}

Here is my own solution that doesn't work properly

open($fh, "$info_file");

while (my $row = <$fh>) {
    chomp $row;
    @tokens = split(":",$row);

    $name     = $tokens[0];
    $operator = $tokens[2];

    if ($name =~ /^[A-Z]/) {

        if ( exists $INFO{$name} ) {
            $ptr = \$INFO{$name};
            push(@ptr, $operator);
        }
        else {
            @array = ( "$operator" );
            $INFO{$name} = [ @array ];
        }
    }
}

close($f);

Upvotes: 1

Views: 109

Answers (3)

Dave Cross
Dave Cross

Reputation: 69244

You're over-complicating massively, I'm afraid.

open my $fh, '<', $info_file or die "Can't open '$info_file': $!";

my %info;

while (<$fh>) {
  next unless /^[A-Z]/;
  chomp;
  my ($name, $operator) = split /:/;
  push @{ $info{$name} }, $operator;
}

And to access it:

foreach my $i (keys %info) {
  foreach my $op (@{ $info{$i} }) {
    say "$i ----> $op";
  }
}

If you treat a hash value as though it's an array reference, then Perl will make it an array reference.

See the Perl Data Structures Cookbook for more details.

Upvotes: 3

Borodin
Borodin

Reputation: 126722

You're very close, but there's no need for the explicit use of references or to initialise a hash value to an empty array

This is all you need

use strict;
use warnings 'all';

open my $fh, '<', $info_file or die $!;

my %info;

while ( <$fh> ) {
    chomp;
    my ($name, $operator) = split /:/;
    push @{ $info{$name} }, $operator if $name =~ /^[A-Z]/;
}

Upvotes: 1

JGNI
JGNI

Reputation: 4013

OK there is a lot wrong with this code firstly, use the three input form of open and don't quote variables by default, Perl knows when things should be strings. So open($fh,"$info_file"); should be open($fh, '<', $info_file);

Second split does not return the separator character by default so $operator = $tokens[2]; should be $operator = $tokens[1];

Third why are you ignoring countries that don't start with A to Z in your file?

Fourth use auto vivification so the whole of the if else block can be replaced by push @{$INFO{$name}}, $operator

Fifth $ptr and @ptr are separate variables assigning an array ref to $ptr does not make it available in @ptr also \$INFO{$name} is taking a reference to what ever $INFO{$name} is, which in your case is already an array ref so you are getting a reference to an array ref in $ptr this should have been written $ptr = $INFO{$name}; if you where keeping this code.

Sixth assigning to @array is redundant the two lines in the else clause should have been written $INFO{$name} = [ $operator ];

With all these changes you get

open($fh, '<', "$info_file");
while (my $row = <$fh>) {
    chomp $row;
    my @tokens = split(":",$row);
    $name = $tokens[0];
    $operator = $tokens[1];
    if ($name =~ /^[A-Z]/) {
        push @{$INFO{$name}}, $operator;
    }
}
close($f);

Upvotes: 2

Related Questions