I am trying to take two arrays of hashes, and convert them to a single data structure. This new structure will be hashref, where the value to each key is an array of hashes.
Below are the two structures I'm converting - which are the result of selectall_arrayref with the slice parameter on two seperate queries:
$mappings = [
{
bill_id => '100',
linked_bill_id => '1000',
},
{
bill_id => '100',
linked_bill_id => '1001',
},
{
bill_id => '200',
linked_bill_id => '2000',
},
{
bill_id => '200',
linked_bill_id => '2001',
},
{
bill_id => '200',
linked_bill_id => '2002',
},
];
$fees = [
{
bill_id => '100',
payment_id => '500',
version => 1,
has_fee => 0,
},
{
bill_id => '100',
payment_id => '501',
version => 2,
has_fee => 1,
},
{
bill_id => '1000',
payment_id => '502',
version => 1,
has_fee => 0,
},
{
bill_id => '1001',
payment_id => '503',
version => 1,
has_fee => 0,
},
{
bill_id => '200',
payment_id => '504',
version => 1,
has_fee => 0,
},
{
bill_id => '2000',
payment_id => '505',
version => 1,
has_fee => 0,
},
{
bill_id => '2001',
payment_id => '506',
version => 1,
has_fee => 0,
},
{
bill_id => '2002',
payment_id => '507',
version => 1,
has_fee => 1,
},
];
Using both structures, I want to create a new structure where the key is the bill_id from $mappings
, and the value is an array of fees for the bill_id and all of its linked_bill_ids. It should like:
$VAR1 = {
'100' => [
{
'bill_id' => '100',
'payment_id' => '500',
'version' => 1,
'has_fee' => 0,
},
{
'bill_id' => '100',
'payment_id' => '501',
'version' => 2,
'has_fee' => 1,
},
{
'bill_id' => '1000',
'payment_id' => '502',
'version' => 1,
'has_fee' => 0,
},
{
'bill_id' => '1001',
'payment_id' => '503',
'version' => 1,
'has_fee' => 0,
},
],
'200' => [
{
'bill_id' => '200',
'payment_id' => '504',
'version' => 1,
'has_fee' => 0,
},
{
'bill_id' => '2000',
'payment_id' => '505',
'version' => 1,
'has_fee' => 0,
},
{
'bill_id' => '2001',
'payment_id' => '506',
'version' => 1,
'has_fee' => 0,
},
{
'bill_id' => '2002',
'payment_id' => '507',
'version' => 1,
'has_fee' => 1,
},
]
};
My code is below, which gets me pretty close but not exactly what I want. Also, I'm convinced that there's a far better method for transforming these data structures than what I'm currently doing. Maybe this can be simplified somehow by chaining maps/greps together for both arrays at once?
I'm looking for help on getting this correct, and advice on how to do it better.
my $bill_fees;
for my $bill (@$mappings) {
push @{ $bill_fees->{bill_fee}{$bill->{bill_id} },
grep { $_->{bill_id} eq $bill->{bill_id} } @$fees;
my @linked_bills = map { $_->{linked_bill_id} }
grep { $_->{bill_id} eq $bill->{bill_id} }
@$mappings;
for my $linked_bill (@linked_bills) {
push @{ $bill_fees->{bill_fee}{$bill->{bill_id}} },
grep { $_->{bill_id} eq $linked_bill } @$fees;
}
}
The method above is slightly incorrect. It gives me the expected structue I listed above, but for each bill_id key I also end up with:
use Data::Dumper;
print Dumper($bill_fees->{bill_fee});
'100' => [
$VAR1->{'100'}[0],
$VAR1->{'100'}[1],
$VAR1->{'100'}[2],
$VAR1->{'100'}[3],
],
'200' => [
$VAR1->{'200'}[0],
$VAR1->{'200'}[1],
$VAR1->{'200'}[2],
$VAR1->{'200'}[3],
$VAR1->{'200'}[0],
$VAR1->{'200'}[1],
$VAR1->{'200'}[2],
$VAR1->{'200'}[3],
]
I assume this is because there are multiple of the same bill_ids in mappings. So, for example, bill_id = 100
would get looped twice.
CodePudding user response:
No changed maps/greps, but easy to read and understand: First transform your mappings array into a hash that really 'maps' to the bill_ids, then iterate over @$fees;
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $mappings = [
{
bill_id => '100',
linked_bill_id => '1000',
},
{
bill_id => '100',
linked_bill_id => '1001',
},
{
bill_id => '200',
linked_bill_id => '2000',
},
{
bill_id => '200',
linked_bill_id => '2001',
},
{
bill_id => '200',
linked_bill_id => '2002',
},
];
my $fees = [
{
bill_id => '100',
payment_id => '500',
version => 1,
has_fee => 0,
},
{
bill_id => '100',
payment_id => '501',
version => 2,
has_fee => 1,
},
{
bill_id => '1000',
payment_id => '502',
version => 1,
has_fee => 0,
},
{
bill_id => '1001',
payment_id => '503',
version => 1,
has_fee => 0,
},
{
bill_id => '200',
payment_id => '504',
version => 1,
has_fee => 0,
},
{
bill_id => '2000',
payment_id => '505',
version => 1,
has_fee => 0,
},
{
bill_id => '2001',
payment_id => '506',
version => 1,
has_fee => 0,
},
{
bill_id => '2002',
payment_id => '507',
version => 1,
has_fee => 1,
},
];
my $bill_fees;
my $bill_ids;
for my $mapping (@$mappings){
$bill_ids->{$mapping->{$_}}=$mapping->{bill_id} for qw/bill_id linked_bill_id/;
}
for my $fee (@$fees){
push @{$bill_fees->{ $bill_ids->{$fee->{bill_id}}}}, $fee;
}
print Dumper $bill_fees;
CodePudding user response:
First, extract a mapping from fees' bill ids to the keys of bill_fees. Then, separate the fees under appropriate bill ids:
my %idmap = map {
$_->{bill_id} => $_->{bill_id},
$_->{linked_bill_id} => $_->{bill_id}
} @$mappings;
my $bill_fees;
for my $fee (@$fees) {
push @{ $bill_fees->{ $idmap{ $fee->{bill_id} } } }, $fee;
}