Yadheendran Yadheendran - 4 months ago 36
Perl Question

Perl pass hash reference to Subroutine from Foreach loop (Array of Hash)

This may be something very simple for you but i have been trying for it for more than one hour.

Ok.. Here is my code,

@collection = [
{
'name' => 'Flo',
'model' => '23423',
'type' => 'Associate',
'id' => '1-23928-2392',
'age' => '23',
},
{
'name' => 'Flo1',
'model' => '23424',
'type' => 'Associate2',
'id' => '1-23928-23922',
'age' => '25',
}];

foreach my $row (@collection) {
$build_status = $self->build_config($row);
}

sub build_config {
my $self = shift;
my %collect_rows = @_;
#my %collect_rows = shift;

print Dumper %collect_rows; exit;
exit;
}


Where $row is really an Hash. But when i Print it.. It gives output like below (I am using Dumper),

$VAR1 = 'HASH(0x20a1d68)';

Answer

You are confusing references with hashes and arrays.

An array is declared with:

my @array = (1, 2, 3);

A hash is declared with:

my %hash = (1 => 2, 3 => 4);

That's because both hashes and arrays are simply lists (fancy lists, but I digress). The only time you need to use the [] and {} is when you want to use the values contained in the list, or you want to create a reference of either list (more below).

Note that the => is just a special (ie. fat) comma, that quotes the left-hand side, so although they do the same thing, $h = (a, 1) would break, $h = (a => 1) works fine, because the a gets quoted.

An array reference is declared as such:

my $aref = [1, 2, 3];

...note that you need to put the array reference into a scalar. If you don't and do it like this:

my @array = [1, 2, 3];

... the reference is pushed onto the first element of @array, which is probably not what you want.

A hash reference is declared like this:

my $href = {a => 1, b => 2};

The only time [] is used on an array (not an aref) is when you're using it to use an element: $array[1];. Likewise with hashes, unless it's a reference, you only use {} to get at a key's value: $hash{a}.

Now, to fix your problem, you can keep using the references with these changes:

use warnings;
use strict;

use Data::Dumper;

# declare an array ref with a list of hrefs

my $collection = [
    {
        'name' => 'Flo',
        ...
    },
    {
        'name' => 'Flo1',
        ...
    }
];

# dereference $collection
# note that $row will always be an href (see bottom of post)

foreach my $row (@{ $collection }) {
    my $build_status = build_config($row);
    print Dumper $build_status;
}

sub build_config {
    # shift, as we're only accepting a single param...
    # the aref

    my $collect_rows = shift;
    return $collect_rows;
}

...or change it up to use non-refs:

my @collection = (
    {
        'name' => 'Flo',
        ...
    },
    {
        'name' => 'Flo1',
        ...
    }
);

foreach my $row (@collection) {
    my @build_status = build_config($row);

    # because @build_status is an array, we must
    # send Dumper a reference to it with \@

    print Dumper \@build_status;
}

sub build_config {
    # we've been passed in an array (list)...
    # we can't use shift() here

    my @collect_rows = @_;

    # return an array

    return @collect_rows;
}

I wrote a tutorial on Perl references you may be interested in guide to Perl references. There's also perlreftut.

One last point... the hashes are declared with {} inside of the array because they are indeed hash references. With multi-dimentional data, only the top level of the structure in Perl can contain multiple values. Everything else underneath must be a single scalar value. Therefore, you can have an array of hashes, but technically and literally, it's an array containing scalars where their value is a pointer (reference) to another structure. The pointer/reference is a single value.