kpiti has asked for the wisdom of the Perl Monks concerning the following question:

Hi wise ones,

I'd like to merge two arrayrefs into one containing all the fields but not repeating them. For example:

my $arr1 = [[qw/name pos loc age/], [qw/ike boss 12 44/], [qw/mat slave 22 21/]]; my $arr2 = [[qw/car dog age/], [qw/a1 grr 3/]]; #...magic goes here and we want: my $arrMighty = [[name, pos, loc, age, car, dog], [ike, boss, 12, 44, "", ""], [mat, slave, 22, 21, "", ""], ["", "", "", 3, a1, grr]];

I don't need to have it in any order as long as the values in the original arrays match their new position (or empty string). Oh yes, the first arefs ($arr1->[0] and $arr2->[0]) are like field names and they merge into one row, the others are data..

I've gone counting the fields and adding them and going trough the arrays over again, it works but it looks like a mess and I'm just wondering if there is some sexy way to do this..

Much obliged for any enlightenments..

Replies are listed 'Best First'.
Re: Merging two array(refs)
by choroba (Cardinal) on Jan 21, 2013 at 00:12 UTC
    Please, can you be more specific? Why do you merge qw/name pos loc age/ with qw/car dog age/, if the first arefs are like field names?

    Also, if you already have a working code, please post it so we can test our solutions against it or propose enhancements.

    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Merging two array(refs)
by kpiti (Novice) on Jan 21, 2013 at 00:26 UTC
    Let me try to be more clear on what I mean:

    in the following arrays qw/name pos loc age/ and qw/car dog age/ the "age" field is present in the first array already, so I don't want to add it again, I want to add the in the "age" (=last) position in the second array to the already existing "age" position in the final combined arref..

    I hope that explains it a bit.

    I can post the code I built up which doesn't look nice, though it does the job.. I'd prefer to have something nicer if possible

    my $arr = [[qw/name pos loc age/], [qw/ike boss 12 44/], [qw/mat slave 22 21/], [qw/jill sec 15 32/],]; my $add = [[qw/car dog age/], [qw/a1 grr 3/], [qw/s2 miew 7/],]; my ($arrfld,$addfld); for ($i=0;$i<@{$arr->[0]};$i++){ $arrfld->{ $arr->[0]->[$i] } = $i } for ($i=0;$i<@{$add->[0]};$i++){ unless ( $arrfld->{ $add->[0]->[$i] } ){ $arrfld->{ $add->[0]->[$i] } = scalar(keys %$arrfld); } $addfld->{ $arrfld->{ $add->[0]->[$i] } } = $i; } foreach my $row (@$arr){ for($x=scalar(@$row);$x<scalar(keys %$arrfld);$x++){push @$row,""} } shift @$add; foreach my $row (@$add){ my $nr; for ($x=0;$x<scalar(keys %$arrfld);$x++){ $nr->[$x] = defined($addfld->{$x})?$row->[$addfld->{$x}]:""; } push @$arr,$nr; }
      Note that you replied to yourself instead of replying to me. Therefore, I did not notice your reply immediately.

      Your code does not do what you claim. No arrays get merged when I run it. Please try to give more details: should the arrays be merged only if the last member is the same, or if any member is the same (i.e., should the script merge qw/a b key c/ with qw/x key y z/)? What would you do if more than one array from the second list could be merged into an array from the first one (i.e. both qw/car dog age/ and qw/train cat age/ in the second list)?

      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        I've been staring at the screen for too long..

        It's funny you say it doesn't merge, I've just copied the code from the post and used Data::Dumper to dump $arr at the end and I can see it merged as it should be..

        No, the initial position of the member doesn't matter. If we look at an arbitrary first row of two arrefs:

        $a->[0] = [qw/f0 f1 f2 f3/]; $b->[0] = [qw/e0 f2 e1 f0 e2];
        If the value of the field in $b->[0] matches one already present in $a->[0] then all values in the matching field in the $b->1..$ arrays should be set in the already matched field. So the final array would be like:
        $m = [ [qw/f0 f1 f2 f3 e0 e1 e2], [qw/a0 a1 a2 a3 "" "" ""/],# ..and so on all of $a [qw/b3 "" b1 "" b0 b2 b4/],# ..and so on for $b->[1..$]
        a0,1,2.. being the first,second,third element of a $a->[x] array and likewise for the b0,1,2 of the $b->[x] array.

        Does this make it any clearer?

Re: Merging two array(refs)
by LanX (Saint) on Jan 21, 2013 at 07:47 UTC
    OK, If I understand correctly you wanna merge the rows of two tables with partly intersecting columns?

    You could use nested slices of position informations of the column-heads

  • to precalculate the positions hash just do something like

    DB<158> @head1= @{$arr1->[0]} => ("name", "pos", "loc", "age") DB<159> @head2= @{$arr2->[0]} => ("car", "dog", "age") DB<161> %pos=();$p=0 DB<162> for ( @head1 , @head2 ) { $pos{$_}=$p++ unless exists $pos{$_} } DB<163> %pos => ("dog", 5, "loc", 2, "car", 4, "name", 0, "pos", 1, "age", 3)

  • now preset the new rows with empty strings and do a list-assign to the calculated positions

    DB<168> @n= ("") x $p #default => ("", "", "", "", "", "") DB<169> @n[ @pos{@head1} ] = @{ $arr1->[1] } => ("ike", "boss", 12, 44) DB<170> @n => ("ike", "boss", 12, 44, "", "") DB<171> @n= ("") x $p #default => ("", "", "", "", "", "") DB<172> @n[ @pos{@head2} ] = @{ $arr2->[1] } => ("a1", "grr", 3) DB<173> @n => ("", "", "", 3, "a1", "grr")

    IMHO the sexiest approach so far! ;-)

    Completing the code is now just a minor task...

    HTH

    Cheers Rolf