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

Hi Monks!

I am trying to merge these two references into one variable,
what is the best way to do this, I am getting this error:

Not a HASH reference

Here is a sample os what I am trying to do:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $data1 = [ { 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'NUMBER' => '00001', }, { 'NAME' => 'ANTHONY RD', 'DATE' => '2012-01-07', 'NUMBER' => '00003', }, ]; my $data2 = [ { 'CAR1' => '1', 'CAR2' => '2', 'CAR3' => '3', }, { 'CAR1' => '1b', 'CAR2' => '2b', 'CAR3' => '3b', }, ]; my $all; push @{ $all }, $data1, $data2; foreach my $row (@$all) { my $name = $row->{ NAME }; my $car1 = $row->{ CAR1 }; warn Dumper $name; warn Dumper $car1; }

Thanks for looking!

Replies are listed 'Best First'.
Re: Combining two references
by Laurent_R (Canon) on Jun 08, 2015 at 17:48 UTC
    jeffa's proposal is probably the simplest, but your code would work if you change the relevant line to:
    push @{ $all }, @$data1, @$data2;
    When I dump $all after this change, I get:
    $VAR1 = [ { 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'NUMBER' => '00001' }, { 'NAME' => 'ANTHONY RD', 'DATE' => '2012-01-07', 'NUMBER' => '00003' }, { 'CAR2' => '2', 'CAR1' => '1', 'CAR3' => '3' }, { 'CAR2' => '2b', 'CAR1' => '1b', 'CAR3' => '3b' } ];
    i.e. presumably what you want.
      This did, is that a way to avoid printing "undef"s:
      my $all; push @{ $all }, @$data1, @$data2; foreach my $row (@$all) { my $name = $row->{ NAME }; my $date = $row->{ DATE }; my $number = $row->{ NUMBER }; my $car1 = $row->{ CAR1 }; my $car2 = $row->{ CAR2 }; my $car3 = $row->{ CAR3 }; warn Dumper $name,$date,$number,$car1,$car2,$car3; }

      Thanks!
Re: Combining two references
by jeffa (Bishop) on Jun 08, 2015 at 17:33 UTC

    De-reference them as arrays inside an array reference, and assign that to a variable:

    my $all = [ @$data1, @$data2 ]; print Dumper $all;

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      Trying this way and getting this error:

      Not a HASH reference at...
      my $all = [ $data1, $data2 ]; foreach my $row ( @{ $all } ) { my $name = $row->{ NAME }; my $car1 = $row->{ CAR1 }; warn Dumper $name; warn Dumper $car1; }

        It fails because you kept the same buggy code instead of using jeffa's fix.

        Again, the fix is to deference the arrays to get their elements: @$data1, @$data2

        That will not work. Since you have a single hash within each array element (and the hashes have colliding key names), you have to extract each hash, and pull the NAME (CAR1 will fail as this hash doesn't have this key). However, the next iteration will overwrite NAME, and still fail on CAR1. On the following two iterations over the array, the exact opposite will happen.

        You will have 'Undefined variable' warnings everywhere.

        Why don't you state what it is that you want to do with the data in the end, and perhaps we can guide you to a data structure that fits your needs?

        -stevieb

Re: Combining two references
by thanos1983 (Parson) on Jun 08, 2015 at 22:08 UTC

    Hello Anonymous,

    Although that the other monks have already answered your question I would like to add something. Why not to use ARRAYS OF HASHES?

    Sample of code provided under:

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my @data1 = ( { 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'NUMBER' => '00001', }, { 'NAME' => 'ANTHONY RD', 'DATE' => '2012-01-07', 'NUMBER' => '00003', } ); print Dumper \@data1; =data1 $VAR1 = [ { 'NUMBER' => '00001', 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05' }, { 'DATE' => '2012-01-07', 'NUMBER' => '00003', 'NAME' => 'ANTHONY RD' } ]; =cut my @data2 = ( { 'CAR1' => '1', 'CAR2' => '2', 'CAR3' => '3', }, { 'CAR1' => '1b', 'CAR2' => '2b', 'CAR3' => '3b', } ); print Dumper \@data2; =data2 $VAR1 = [ { 'CAR3' => '3', 'CAR1' => '1', 'CAR2' => '2' }, { 'CAR3' => '3b', 'CAR1' => '1b', 'CAR2' => '2b' } ]; =cut push my @AoH , ( @data1 , @data2 ); =alternatively my @AoH = ( @data1 , @data2 ); =cut print Dumper \@AoH; __END__ $VAR1 = [ { 'NUMBER' => '00001', 'DATE' => '2009-05-05', 'NAME' => 'PAUL DY' }, { 'NUMBER' => '00003', 'NAME' => 'ANTHONY RD', 'DATE' => '2012-01-07' }, { 'CAR3' => '3', 'CAR2' => '2', 'CAR1' => '1' }, { 'CAR1' => '1b', 'CAR2' => '2b', 'CAR3' => '3b' } ];

    There are many ways to play around with these just try.

    For example you can Making References with array of hashes.

    Sample of code:

    my $refAoH = \@AoH; print Dumper $refAoH;

    Same result as above.

    Hope this helps.

    Seeking for Perl wisdom...on the process of learning...not there...yet!
      Whats in your option should be the most efficient way to interact (loop) over this ARRAYS OF HASHES?

      Thanks!
Re: Combining two references
by RichardK (Parson) on Jun 08, 2015 at 17:37 UTC

    Firstly, which line is giving you that error ?

    Secondly, why not dump $all to see what it contains?

    see also : basic debugging checklist

      "Firstly, which line is giving you that error ?"

      You should be able to determine that. Which is the first line that tries to use something as a hash reference? This one:

      my $name = $row->{ NAME };
Re: Combining two references
by CountZero (Bishop) on Jun 09, 2015 at 16:06 UTC
    Through the magic of List::MoreUtils:
    use Modern::Perl qw/2015/; use Data::Dump qw/dump/; use List::MoreUtils qw/zip natatime/; my $data1 = [ { 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'NUMBER' => '00001', }, { 'NAME' => 'ANTHONY RD', 'DATE' => '2012-01-07', 'NUMBER' => '00003', }, ]; my $data2 = [ { 'CAR1' => '1', 'CAR2' => '2', 'CAR3' => '3', }, { 'CAR1' => '1b', 'CAR2' => '2b', 'CAR3' => '3b', }, ]; my @all; my $it = natatime 2, zip @$data1, @$data2; while (my ($first, $second) = $it->()) { push @all, {%$first, %$second}; } say dump @all;
    Output:
    ( { CAR1 => 1, CAR2 => 2, CAR3 => 3, DATE => "2009-05-05", NAME => "PAUL DY", NUMBER => "00001", }, { CAR1 => "1b", CAR2 => "2b", CAR3 => "3b", DATE => "2012-01-07", NAME => "ANTHONY RD", NUMBER => "00003", }, )

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics
      What if you just want to merge these two arrays:

      ... my $data1 = [ { 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'NUMBER' => '00001', }, ]; my $data2 = [ { 'CAR1' => '1b', 'CAR2' => '2b', 'CAR3' => '3b', 'CAR4' => '3d', }, ]; ...

      into:
      my $data3 = [ { 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'NUMBER' => '00001', 'CAR1' => '1b', 'CAR2' => '2b', 'CAR3' => '3b', 'CAR4' => '3d', }, ];

      Thanks!
        Why don't you try and see what it does?

        I'll wait while you try. ......

        See, it "just worked". Ain't Perl nice?

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics
Re: Combining two references
by locked_user sundialsvc4 (Abbot) on Jun 09, 2015 at 13:59 UTC

    Whereas, I came up with a more pedantic solution, something like this:

    use strict; use warnings; use Data::Dumper; my $data1 = [ { 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'NUMBER' => '00001', }, { 'NAME' => 'ANTHONY RD', 'DATE' => '2012-01-07', 'NUMBER' => '00003', }, ]; my $data2 = [ { 'CAR1' => '1', 'CAR2' => '2', 'CAR3' => '3', }, { 'CAR1' => '1b', 'CAR2' => '2b', 'CAR3' => '3b', }, ]; my $i=0; my @result; foreach my $row1 (@$data1) { my $record={}; while (my ($k1, $v1) = each(%$row1)) {$record->{$k1}=$v1}; my $row2=@$data2[$i]; while (my ($k2, $v2) = each(%$row2)) {$record->{$k2}=$v2}; push @result, $record; $i++; } use Data::Dumper; print Data::Dumper->Dump([\@result], ["result"]);
    which gives:
    $result = [ { 'CAR2' => '2', 'NAME' => 'PAUL DY', 'DATE' => '2009-05-05', 'CAR1' => '1', 'CAR3' => '3', 'NUMBER' => '00001' }, { 'CAR2' => '2b', 'NAME' => 'ANTHONY RD', 'DATE' => '2012-01-07', 'CAR1' => '1b', 'CAR3' => '3b', 'NUMBER' => '00003' } ];

    I simply used a foreach loop to iterate over $data1, then used variable $i which is incremented within the loop to fetch the rows from $data2.   I attempted to use consistent variable-names in both cases.   I made no attempt to be clever nor efficient:   I wanted the code to be “obvious.”

    One thing to keep firmly in mind, when doing things like this, is that you can easily wind up manipulating references to things, within the output that you create ... the old FORTRAN EQUIVALENCE statement ... when you might need to be making “actual copies.”   If you are moving or creating references, any change will be reflected in all references to that same thing, which might not be what you wanted nor expected.

      my $row2=@$data2[$i];

      That way of accessing a single element of an array is usually discouraged unless you know what you're doing. Better is $data2->[$i].