Reputation: 543
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
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
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
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