Question

I want to write a little "DBQuery" function in perl so I can have one-liners which send an SQL statement and receive back and an array of hashes, i.e. a recordset. However, I'm running into an issue with Perl syntax (and probably some odd pointer/reference issue) which is preventing me from packing out the information from the hash that I'm getting from the database. The sample code below demonstrates the issue.

I can get the data "Jim" out of a hash inside an array with this syntax:

print $records[$index]{'firstName'}

returns "Jim"

but if I copy the hash record in the array to its own hash variable first, then I strangely can't access the data anymore in that hash:


    %row = $records[$index];
    $row{'firstName'};

returns "" (blank)

Here is the full sample code showing the problem. Any help is appreciated:


my @records = (
   {'id' => 1, 'firstName' => 'Jim'},
   {'id' => 2, 'firstName' => 'Joe'}
);
my @records2 = ();

$numberOfRecords = scalar(@records);
print "number of records: " . $numberOfRecords . "\n";
for(my $index=0; $index < $numberOfRecords; $index++) {

   #works
   print 'you can print the records like this: ' . $records[$index]{'firstName'} . "\n";

   #does NOT work
   %row = $records[$index];
   print 'but not like this: ' . $row{'firstName'} . "\n";

} 
Was it helpful?

Solution

The nested data structure contains a hash reference, not a hash.

# Will work (the -> dereferences the reference)
$row = $records[$index];
print "This will work: ", $row->{firstName}, "\n";

# This will also work, by promoting the hash reference into a hash
%row = %{ $records[$index] };
print "This will work: ", $row{firstName}, "\n";

If you're ever presented with a deep Perl data structure, you may profit from printing it using Data::Dumper to print it into human-readable (and Perl-parsable) form.

OTHER TIPS

The array of hashes doesn't actually contain hashes, but rather an references to a hash. This line:

%row = $records[$index];

assigns %row with one entry. The key is the scalar:

   {'id' => 1, 'firstName' => 'Jim'},

Which is a reference to the hash, while the value is blank.

What you really want to do is this:

$row = $records[$index];
$row->{'firstName'};

or else:

$row = %{$records[$index];}
$row{'firstName'};

Others have commented on hashes vs hashrefs. One other thing that I feel should be mentioned is your DBQuery function - it seems you're trying to do something that's already built into the DBI? If I understand your question correctly, you're trying to replicate something like selectall_arrayref:

This utility method combines "prepare", "execute" and "fetchall_arrayref" into a single call. It returns a reference to an array containing a reference to an array (or hash, see below) for each row of data fetched.

To add to the lovely answers above, let me add that you should always, always, always (yes, three "always"es) use "use warnings" at the top of your code. If you had done so, you would have gotten the warning "Reference found where even-sized list expected at -e line 1."

what you actually have in your array is a hashref, not a hash. If you don't understand this concept, its probably worth reading the perlref documentation.

to get the hash you need to do

my %hash = %{@records[$index]};

Eg.

my @records = (
     {'id' => 1, 'firstName' => 'Jim'}, 
     {'id' => 2, 'firstName' => 'Joe'}
  );

  my %hash = %{$records[1]};

  print $hash{id}."\n";

Although. I'm not sure why you would want to do this, unless its for academic purposes. Otherwise, I'd recommend either fetchall_hashref/fetchall_arrayref in the DBI module or using something like Class::DBI.

Also note a good perl idiom to use is

for my $rowHR ( @records ) {
   my %row = %$rowHR; 
   #or whatever...
}

to iterate through the list.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top