Friday, October 7, 2022

[SOLVED] Mapping two arrays of hashes to a new data structure

Issue

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.


Solution

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;
}

Note how each bill id links to itself, too, to simplify the retrieval.



Answered By - choroba
Answer Checked By - Marie Seifert (WPSolving Admin)