Reputation: 21
This is my text file:
animal, cola, husband, 36
animal, wilma, wife, 31
animal, pebbles, kid, 4
brutal, george, husband, 41
brutal, jane, wife, 39
brutal, elroy, kid, 9
cosa, homer, husband, 34
cosa, marge, wife, 37
cosa, bart, kid, 11
And this is the data structure I want:
%HASH = (
animal => [
{ name => "cola", role => "husband", age => 36, },
{ name => "wilma", role => "wife", age => 31, },
{ name => "pebbles", role => "kid", age => 4, },
],
brutal => [
{ name => "george", role => "husband", age => 41, },
{ name => "jane", role => "wife", age => 39, },
{ name => "elroy", role => "kid", age => 9, },
],
cosa => [
{ name => "homer", role => "husband", age => 34, },
{ name => "marge", role => "wife", age => 37, },
{ name => "bart", role => "kid", age => 11, },
],
);
I have some pieces of code, but I can't assemble them into a coherent script. I want only for someone to help me to define this structure and to understand it.
Upvotes: 0
Views: 345
Reputation: 66891
The data structure you show has one critical rule: A value in a hash can only be a scalar.
So to associate a multi-valued variable with a key use the reference to that variable, here arrayref. And if values in that array need be more complex than scalars you again use a reference, here a hashref.† So the value for each key is an arrayref whose elements are hashrefs.
Then you need to learn how to access elements deeper in the structure. That isn't very complex either: dereference them at each level and you can then work with them like you would with an array or hash.
All this is in perldsc, for which one need be clear with perlreftut. A reference is perlref.
When this is put to use in your problem
use warnings;
use strict;
use Data::Dump qw(dd);
my $file = 'data.txt';
open my $fh, '<', $file or die "Can't open $file: $!";
my %result;
while (<$fh>) {
chomp;
my ($key, $name, $role, $age) = split /\s*,\s*/;
push @{$result{$key}},
{ name => $name, role => $role, age => $age };
}
dd \%result;
This prints the correct data structure. I use Data::Dump to see a complex data structure, which need be installed; there is Data::Dumper in the core. There are yet others.
The split above uses a regex /\s*,\s*/
for the delimiter, so to split the line by comma optionally surrounded by spaces. The default for the string to split is $_
.
Note that we don't have to "add a key" or make its arrayref-value ahead of using them, as that's done via autovivification. See for example this page and this page and this page. It is a complex feature that can bite if misused so please read up on it.
† If we attempt to use array or hash variables for array elements we are really trying to fit a list of values into a single "slot" of an array. That can't be done of course, and what happens is that they'll get "flattened" – their elements are merged with all other given scalar elements, and that whole aggregate list populates the array
my @ary = 1..3;
my %hash = (a => 10, b => 20);
# Most likely an error:
my @all = (5, @ary, %hash, 100); #--> (5, 1, 2, 3, a, 10, b, 20, 100)
where key-value pairs may come in any order since hashes are inherently unordered.
Instead, we take references to arrays and hashes and write
my @all = (5, \@ary, \%hash, 100);
Since references are scalars they are legit elements of the array and no flattening happens. So now contents of @ary
and %hash
keep their individuality and can be recovered as needed.
Upvotes: 2
Reputation: 6553
Code:
use strict;
use warnings;
use Data::Dumper;
my %hash;
my @columns = qw(category name role age);
while (<DATA>) {
chomp;
my %temp;
@temp{@columns} = split(/\s*,\s*/);
my $key = delete($temp{category});
push(@{$hash{$key}}, \%temp);
}
print Dumper(\%hash);
__DATA__
animal, cola, husband, 36
animal, wilma, wife, 31
animal, pebbles, kid, 4
brutal, george, husband, 41
brutal, jane, wife, 39
brutal, elroy, kid, 9
cosa, homer, husband, 34
cosa, marge, wife, 37
cosa, bart, kid, 11
Output:
$VAR1 = {
'cosa' => [
{
'name' => 'homer',
'age' => '34',
'role' => 'husband'
},
{
'name' => 'marge',
'age' => '37',
'role' => 'wife'
},
{
'name' => 'bart',
'age' => '11',
'role' => 'kid'
}
],
'brutal' => [
{
'name' => 'george',
'age' => '41',
'role' => 'husband'
},
{
'name' => 'jane',
'age' => '39',
'role' => 'wife'
},
{
'name' => 'elroy',
'age' => '9',
'role' => 'kid'
}
],
'animal' => [
{
'name' => 'cola',
'age' => '36',
'role' => 'husband'
},
{
'name' => 'wilma',
'age' => '31',
'role' => 'wife'
},
{
'name' => 'pebbles',
'age' => '4',
'role' => 'kid'
}
]
};
Upvotes: 2