Perl group cast to hash using fetchrow_hashref
I want to group my results by countryid with the data shown below.
my @test = ();
my $st = qq[
SELECT id,countryID,abbrev
FROM myTable
];
my $q = $data->prepare($st);
$q->execute() or query_error($st);
while ( my $result = $q->fetchrow_hashref ) {
push @test, $result;
}
Using fetchrow_hashref I have no problem displaying the results
use Data::Dumper;
print STDERR Dumper(\@test);
returns
$VAR1 = [
{ 'id' => '1',
'countryID' => '1',
'title' => 'Title 1',
'abbrev' => 't1'
},
{ 'id' => '2',
'countryID' => '2',
'title' => 'Title 2',
'abbrev' => 't2'
},
{ 'id' => '3',
'countryID' => '3',
'title' => 'Title 3',
'abbrev' => 't3'
},
{ 'id' => '4',
'countryID' => '1',
'title' => 'Title 4',
'abbrev' => 't4'
}
];
I want to group it by country as shown below.
$VAR1 = [
'countries' => {
'1' => [
{ 'id' => '1',
'title' => 'Title 1',
'abbrev' => 't1'
},
{ 'id' => '4',
'title' => 'Title 4',
'abbrev' => 't4'
}
],
'2' => [
{ 'id' => '2',
'title' => 'Title 2',
'abbrev' => 't2'
}
],
'3' => [
{ 'id' => '3',
'title' => 'Title 3',
'abbrev' => 't3'
}
]
}
];
How can I get this to work in a while loop?
source to share
You need to correct your syntax a bit (like =>
instead = >
), but once you've done that, something like this should work well.
for (@$VAR1_orig) {
my %a = %$_;
my $countryID = $a{countryID};
delete $a{countryID};
push @{$VAR1->{countries}{$countryID}}, \%a;
}
(By the way, I tried this on my computer, it works.)
The above assumes it is %$VAR1
initially empty, then fills it in according to @$VAR1_orig
, after which you can do with $VAR1
whatever you like. (I assume you know what Perl means as %$
well @$
, but this is not a newbie as you know. See man 1 perlref
.)
source to share
Ignoring errors in your sample data structure, you basically want to convert an array of hash shapes to a hash hash of an array of hashes. After setting up the original data structure, you can do the following to create a new nested data structure:
for my $href ( @test ) {
my $id = $href->{countryID};
delete $href->{countryID};
push @{ $test2->{countries}{$id} }, $href;
}
Iterate over each element of your array @test
, which is basically an array of hash references. Create a variable $id
that will grab the value countryID
from the hash. We remove it from the hash reference and then assign this hash reference to our new nested data structure, which has countries
both the first level key
and $id
the second level key.
We use the syntax push
to create our array of such links.
Note: As thb pointed out in the comments, this destroys your original data structure. If you want to keep the original structure, change your code to the following:
for my $href ( @test ) {
my $copy = { %$href };
my $id = $copy->{countryID};
delete $copy->{countryID};
push @{ $test2->{countries}{$id} }, $copy;
}
source to share
Something like this, the I / O data structures may not be exactly what you have or want, you can fix that.
use strict;
use Data::Dumper;
$a = [
{ 'id' => '1',
'countryID' => '1',
'title' => 'Title 1',
'abbrev' => 't1'
},
{ 'id' => '2',
'countryID' => '2',
'title' => 'Title 2',
'abbrev' => 't2'
},
{ 'id' => '3',
'countryID' => '3',
'title' => 'Title 3',
'abbrev' => 't3'
},
{ 'id' => '4',
'countryID' => '1',
'title' => 'Title 4',
'abbrev' => 't4'
}
];
my $b = {};
for my $item (@$a) {
if ( exists( $b->{ $item->{'countryID'} } ) ) {
push( @{ $b->{ $item->{'countryID'} } }, $item );
} else {
$b->{ $item->{'countryID'} } = [$item];
}
}
print Dumper($b);
The above prints:
$VAR1 = {
'1' => [
{ 'abbrev' => 't1',
'title' => 'Title 1',
'id' => '1',
'countryID' => '1'
},
{ 'abbrev' => 't4',
'title' => 'Title 4',
'id' => '4',
'countryID' => '1'
}
],
'3' => [
{ 'abbrev' => 't3',
'title' => 'Title 3',
'id' => '3',
'countryID' => '3'
}
],
'2' => [
{ 'abbrev' => 't2',
'title' => 'Title 2',
'id' => '2',
'countryID' => '2'
}
]
};
source to share