Bad index while coercing array into hash in Perl - perl

Below is the code
my $results = $session->array_of_hash_for_cursor("check_if_receipts_exist", 0, #params);
next if( scalar #{$results} <= 0 );
$logger->info("Retrieved number of records: ".scalar #$results);
foreach my $row( sort { $results->{$b}->{epoch_received_date} cmp $results->{$a}->{epoch_received_date} } keys %{$results} )
{
//logic
}
'check_if_receipts_exists' is a SQL query which returns some results. Which I try to execute this, I am getting the following error,
Bad index while coercing array into hash
I am new to Perl. Can someone please point out the mistake I am making?

Is $results a hash reference or an array reference?
In some places you are using it like an array reference:
scalar #{$results}
and in other places you are using it like a hash reference:
$results->{$b}->{...}
keys %{$results}
It can't be both (at least not without some heavy overload magic).
If I can infer from the name of the function that sets $results, it should be a reference to a list of hash references, then a few tweaks will set it right:
Using #{$results} is correct; this expression is "an array of hash references"
The last argument to sort should be a list, but the correct list to pass is #{$results}, not keys %{$results}.
Then the parameters $a and $b inside the sort function will be members of #{$results}, that is, they will be hash references. So the comparison to make is
$a->{epoch_received_date} cmp $b->{epoch_retrieve_data}
and not
$results->{$a}->{...} cmp $results->{$b}->{...}
All together:
my $results = $session->array_of_hash_for_cursor(
"check_if_receipts_exist", 0, #params);
next if !#$results;
$logger->info("Retrieved number of records: ".#$results);
for my $row (
sort {
$b->{epoch_received_date}
cmp
$a->{epoch_received_date}
} #$results
) {
# logic
}

Related

Understanding the 'foreach' syntax for the keys of a hash

I have created a simple Perl hash
Sample.pl
$skosName = 'foo';
$skosId = 'abc123';
$skosFile{'type'}{$skosId} = $skosName;
Later on I try to print the hash values using foreach.
This variant works
foreach $skosfile1type ( keys %{skosFile} ){
print ...
}
While this one doesn't
foreach $skosfile1type ( keys %{$skosFile} ) {
print ...
}
What is the difference between the two foreach statements?
In particular, what is the significance of the dollar sign $ in the statement that doesn't work?
Is it something to do with scope, or perhaps my omission of the my or our keywords?
%{skosfile} is the same as %skosfile. It refers to a hash variable with that name. Usually that form isn't used for a simple variable name, but it's allowable.
%{$skosfile} means to look at the scalar variable $skosfile (remember, in perl, $foo, %foo, and #foo are distinctvariables), and, expecting $skosfile to be a hashref, it returns the hash that the reference points to. It is equivalent to %$skosfile, but in fact any expression that returns a hashref can appear inside of %{...}.
The syntax %{ $scalar } is used to tell Perl that the type of $scalar is a hash ref and you want to undo the reference. That is why you need the dollar sign $: $skosfile is the variable you are trying to dereference.
In the same fashion, #{ $scalar } serves to dereference an array.
Although it does not work for complex constructions, in simple cases you may also abbreviate %{$scalar} to %$scalar and #{$scalar} to #$scalar.
In the case of the expression keys %{$skosfile}, keys needs a hash which you obtain by dereferencing $skosfile, a hash ref. In fact, the typical foreach loop for a hash looks like:
foreach my $key ( keys %hash ) {
# do something with $key
}
When you iterate a hash ref:
foreach my $key ( keys %{ $hashref } ) {
# do something with $key
}

Why is 1 assigned to a hash element?

Could any one explain what this statement does in Perl
$type{$_->{brand}} = 1;
I could understand that the hash %type has a key brand holding the reference to another hash brand and 1 is assigned to it
what does it mean??!!! when it is assigned as 1?
package SillyFunction;
sub group_products {
my $products = shift;
my %brand_type = ();
my $grouped_products = [];
foreach (#{$products}) {
$brand_type{ $_->{brand} } ||= {};
$brand_type{ $_->{brand} }->{ $_->{type} } = 1;
}
foreach (sort keys %brand_type) {
my $brand = $_;
foreach (sort keys %{ $brand_type{$brand} }) {
push(#{$grouped_products}, { brand => $brand, type => $_ });
}
}
$grouped_products;
}
1;
The code
$type{$_->{brand}} = 1;
means:
We have a variable of the type hash, named %hash.
The topic variable $_ contains a reference to a hash.
We access the entry called brand in the hash referenced by $_. We remember this value.
We access the entry with the name we just remembered in the hash named %hash.
Hash elements are lvalues, i.e. something can be assigned to them.
We assign the number 1 into the hash slot we just accessed.
Points to note:
In Perl, a hash is a data structure. Other languages know this as an associative array. It maps strings to scalar values.
A hash function calculates a characteristic number for a given string. The hash data structure uses such a function internally, and in a way inaccessible from Perl. Hash functions are also important in cryptography.
The = operator assigns the thing on the right to the thing on the left.
That line of code has not a single keyword, only variables (%type, $_), constants ('brand', 1) and operators ({...}, ->, =, ;).
Here is the code you posted in your comment, annotated with comments:
# Declare a namespace "SillyFunction".
# This affects the full names of the subroutines, and some variables.
package SillyFunction;
# Declare a sub that takes one parameter.
sub group_products {
my $products = shift;
my %brand_type = (); # %brand_type is an empty hash.
my $grouped_products = []; # $grouped_products is a reference to an array
# loop through the products.
# The #{...} "dereferences" an arrayref to an ordinary array
# The current item is in the topic variable $_
foreach (#{$products}) {
# All the items in $products are references to hashes.
# The hashes have keys "brand" and "type".
# If the entry if %brand_type with the name of $_->{brand} is false,
# Then we assign an empty hashref.
# This is stupid (see discussion below)
$brand_type{$_->{brand}} ||= {};
# We access the entry names $_->{brand}.
# We use that value as a hashref, and access the entry $_->{type} in there.
# We then assign the value 1 to that slot.
$brand_type{$_->{brand}}->{$_->{type}} = 1;
}
# We get the names of all entries of %brand_type with the keys function
# We sort the names alphabetically.
# The current key is in $_
foreach (sort keys %brand_type) {
# We assign the current key to the $brand variable.
# This is stupid.
my $brand = $_;
# We get all the keys of the hash referenced by $brand_type{$brand}
# And sort that again.
# The current key is in $_
foreach (sort keys %{$brand_type{$brand}}) {
# We dereference the ordinary array from the arrayref $grouped_products.
# We add a hashref to the end that contains entries for brand and type
push(#{$grouped_products}, { brand => $brand, type => $_});
}
}
# We implicitly return the arrayref containing all brands and types.
$grouped_products;
}
# We return a true value to signal perl that this module loaded all right.
1;
What does this code do? It takes all products (a product is a hashref containing a field for brand and type), and sorts them primarily by brand, secondarily by type, in alphabetic, ascending order.
While doing so, the author produced horrible code. Here is what could have gone better:
He uses an arrayref instead of an array. It would have been easier to just use an array, and return a reference to that:
my #grouped_products;
push #grouped_products, ...;
return \#grouped_products; # reference operator \
At some point, an hashref is assigned. This is unneccessary, as Perl autovivicates undefined values that you use as a hash or array reference. That complete line is useless. Also, it is only assigned if that value is false. What the author probably wanted is to assign if that value is undefined. The defined-or operator // could have been used here (only since perl5 v10 or later).
A hash of hashes is built. This is wasteful. A hash of an array would have been better.
If one loops over values with for or foreach, the current item doesn't have to be assigned to the cryptic $_. Instead, a loop variable can be specified: foreach my $foo (#bar). The default behaviour of foreach is similar to foreach local $_ (#bar).
Implicit returns are bad.
Here is a piece of code that implements the same subroutine, but more perlish — remember, we just wanted to sort the products (assuming they already are unique)
sub group_products {
my ($products) = #_;
my #grouped =
# sort by brand. If that is a draw, sort by type.
sort { $a->{brand} cmp $b->{brand} or $a->{type} cmp $b->{type} }
map { +{%$_} } # make a copy.
#$products; # easy dereference
return \#grouped;
}
Explanation: This code is largely self-documenting. The sort function takes a block that has to return a number: Either negative for “$a is smaller than $b”, zero for “$a and $b are equal”, or positive for “$a is larger than $b”.
The cmp operator compare the operands lexigraphically. If the brands are different, then we don't have to compare the types. If the brands are the same, then the first cmp returns 0, which is a false value. Therefore, the second comparision (type) is executed, and that value returned. This is standard Perl idiom for sorting by primary and secondary key.
The sort and map cascade executes from right/bottom to left/top.
If the uniqueness is not guaranteed, something like this would work better:
use List::MoreUtils qw/uniq/;
sub group_products {
my ($products) = #_;
my %grouping;
push #{ $grouping{ $_->{brand} } }, $_->{type} for #$products;
my #grouped;
for my $brand (sort keys %grouping) {
push #grouped, +{brand => $brand, type => $_} for sort uniq #{ $grouping{$brand} };
}
return \#grouped;
}
Explanation: We define a %grouping hash (to be filled). For each product, we add the type of that product to the arrayref of the appropriate brand in the grouping hash. That is, we collect all types for each brand. We define an array of all grouped products (to be filled). We iterate through all brands in alphabetical order, and then iterate through all unique products of that brand in alphabetical order. For each of these brand/type combinations, we add a new hashref to the grouped products. The uniq function is imported from the excellent List::MoreUtils module. We return a reference to the array of grouped products.

How to manipulate a hash-ref with Perl?

Take a look at this code. After hours of trial and error. I finally got a solution. But have no idea why it works, and to be quite honest, Perl is throwing me for a loop here.
use Data::Diff 'Diff';
use Data::Dumper;
my $out = Diff(\#comparr,\#grabarr);
my #uniq_a;
#temp = ();
my $x = #$out{uniq_a};
foreach my $y (#$x) {
#temp = ();
foreach my $z (#$y) {
push(#temp, $z);
}
push(#uniq_a, [$temp[0], $temp[1], $temp[2], $temp[3]]);
}
Why is it that the only way I can access the elements of the $out array is to pass a hash key into a scalar which has been cast as an array using a for loop? my $x = #$out{uniq_a}; I'm totally confused. I'd really appreciate anyone who can explain what's going on here so I'll know for the future. Thanks in advance.
$out is a hash reference, and you use the dereferencing operator ->{...} to access members of the hash that it refers to, like
$out->{uniq_a}
What you have stumbled on is Perl's hash slice notation, where you use the # sigil in front of the name of a hash to conveniently extract a list of values from that hash. For example:
%foo = ( a => 123, b => 456, c => 789 );
$foo = { a => 123, b => 456, c => 789 };
print #foo{"b","c"}; # 456,789
print #$foo{"c","a"}; # 789,123
Using hash slice notation with a single element inside the braces, as you do, is not the typical usage and gives you the results you want by accident.
The Diff function returns a hash reference. You are accessing the element of this hash that has key uniq_a by extracting a one-element slice of the hash, instead of the correct $out->{uniq_a}. Your code should look like this
my $out = Diff(\#comparr, \#grabarr);
my #uniq_a;
my $uniq_a = $out->{uniq_a};
for my $list (#$uniq_a) {
my #temp = #$list;
push #uniq_a, [ #temp[0..3] ];
}
In the documentation for Data::Diff it states:
The value returned is always a hash reference and the hash will have
one or more of the following hash keys: type, same, diff, diff_a,
diff_b, uniq_a and uniq_b
So $out is a reference and you have to access the values through the mentioned keys.

Test, if a hash key exists and print values of Hash in perl

If a key exists in a array, I want to print that key and its values from hash. Here is the code I wrote.
for($i=0;$i<#array.length;$i++)
{
if (exists $hash{$array[$i]})
{
print OUTPUT $array[$i],"\n";
}
}
From the above code, I am able to print keys. But I am not sure how to print values of that key.
Can someone help me?
Thanks
#array.length is syntactically legal, but it's definitely not what you want.
#array, in scalar context, gives you the number of elements in the array.
The length function, with no argument, gives you the length of $_.
The . operator performs string concatenation.
So #array.length takes the number of elements in #array and the length of the string contained in $_, treats them as strings, and joins them together. $i < ... imposes a numeric context, so it's likely to be treated as a number -- but surely not the one you want. (If #array has 15 elements and $_ happens to be 7 characters long, the number should be 157, a meaningless value.)
The right way to compute the number of elements in #array is just #array in scalar context -- or, to make it more explicit, scalar #array.
To answer your question, if $array[$i] is a key, the corresponding value is $hash{$array[$i]}.
But a C-style for loop is not the cleanest way to traverse an array, especially if you only need the value, not the index, on each iteration.
foreach my $elem (#array) {
if (exists $hash{$elem}) {
print OUTPUT "$elem\n";
}
}
Some alternative methods using hash slices:
foreach (#hash{#array}) { print OUTPUT "$_\n" if defined };
print OUTPUT join("\n",grep {defined} #hash{#array});
(For those who like golfing).

some questions on a perl program

With respect to the following code segment, I would like to know whether my understanding on several issues are correct?
1) In the structure of $model->{in1}->{tra1}->{data}} , “in1”, “tra1”, and “data” all represent specific keys at different levels of hash structures.
2) Does $#{$model->{in1}->{tra1}->{data}}represent an array?
3) What does my #cus = sort keys %cus; aim to do? Are the “cus” at the right side and the “cus” at the left side the same thing?
my %cus = ();
for my $i ( 0 .. $#{$model->{in1}->{tra1}->{data}})
{
foreach my $cu (keys %{$model->{in1}->{tra1}->{data}->[$i]->{concept}}
{
$cus{$cu} = 1;
}
}
my #cus = sort keys %cus;
1)
They are keys to different hashes, yes.
in1 is used as the key to the hash referenced by $model.
tra1 is used as the key to the hash referenced by $model->{in1}.
data is used as the key to the hash referenced by $model->{in1}->{tra1}.
2)
$#a returns the last index of array #a.
so
$#{ $ref } (or $#$ref for short) returns the last index of #{ $ref } (or #$ref for short), the array referenced by $ref.
so
$#{ $model->{in1}->{tra1}->{data} } returns the last index of #{ $model->{in1}->{tra1}->{data} }, the array referenced by $model->{in1}->{tra1}->{data}.
3)
The statement sorts the keys of the hash %cus and places them in array #cus. No, %cus and #cus aren't the same variable.
"4")
The code can be simplified to:
my %cus;
my $data = $model->{in1}->{tra1}->{data};
for my $i (0 .. $#$data) {
for my $cu (keys %{ $data->[$i]->{concept} }) {
++$cus{$cu};
}
}
my #cus = sort keys %cus;
Or even:
my %cus;
for my $data_item (#{ $model->{in1}->{tra1}->{data} }) {
for my $cu (keys %{ $data_item->{concept} }) {
++$cus{$cu};
}
}
my #cus = sort keys %cus;
Yes, you've got nested hashes three deep.
Yes, the $#{...} part means "the largest index of the enclosed array". You also know that ...->{data} is a (reference to an) array because of the ->{data}->[$i] on the next line.
#cus and %cus are two different variables, unrelated.
In the structure of $model->{in1}->{tra1}->{data}} , “in1”, “tra1”, and “data” all represent specific keys at different levels of hash structures.
Yes. If that is not the case, there will be an error.
Does $#($model->{in1}->{tra1}->{data}} represent an array?
Not quite. It is the number of elements in an array (so, yes, the data in the hash should be an array).
What does my #cus = sort keys %cus; aim to do?
It takes all the keys from the hash table %cus and sorts them alphabetically into a new array #cus.
Are the “cus” at the right side and the “cus” at the left side the same thing?
No. In Perl $cus, #cus and %cus are three different variables. The prefix denotes the type.