Error when accessing array of hashes - arrays

I'm trying to generate a list of hashes, interacting with the user using the following script:
use strict;
use warnings;
my $KEY_1 = "one";
my $KEY_2 = "two";
sub generateHash{
my ($value1, $value2) = (#_);
$value2 = $value1 + 5.0;
my %hash = {};
$hash{$KEY_1} = $value1;
$hash{$KEY_2} = $value2;
return %hash;
}
print "Num: \n";
my $number = <>;
my #hashes = ();
my %new_hash = {};
for ( my $i = 1; $i < $number + 1; $i = $i + 1 ) {
print "Enter the values $i \n";
print "value 1: ";
my $value1= <>;
print "\nvalue 2: ";
my $value2= <>;
chomp $value1;
chomp $value2;
%new_hash = generateHash($value1, $value2);
push (#hashes, %new_hash);
print "#hashes\n";
}
my %test = $hashes[0];
my #keys = keys %test;
my #values = values %test;
print "#keys\n";
print "#values\n";
When I try to execute the program, it raises some errors related to use of references when accessing to the hashes within the array. I'm missing something but I can't see what, I wonder where I'm accessing to hash's reference. Thank you in advance, attached is the output from the execution:
Num:
1
Reference found where even-sized list expected at generate_hashes.pl line 21, <> line 1.
Enter the values 1
value 1: 1
value 2: 1
Reference found where even-sized list expected at generate_hashes.pl line 12, <> line 3.
Use of uninitialized value $hashes[1] in join or string at generate_hashes.pl line 32, <> line 3.
HASH(0x2587a88) one 1 two 6
Odd number of elements in hash assignment at generate_hashes.pl line 34, <> line 3.
HASH(0x2587a88)
Use of uninitialized value $values[0] in join or string at generate_hashes.pl line 38, <> line 3.

Reference found where even-sized list expected at generate_hashes.pl line 21, <> line 1.
That one is due to
my %new_hash = {};
Empty curly braces {} provide a reference to an empty hash, but on the left hand side you have a hash, not a reference to one. You could instead initialise using:
my %new_hash = ();
But (as commented) you don't in practice need to initialise if you want to start with an empty hash; that's the default.
Reference found where even-sized list expected at generate_hashes.pl line 12, <> line 3.
Same fault as above inside the generateHash function.
In this line:
push (#hashes, %new_hash);
I suspect you intend to push a reference to the hash into the array, to do that you need to add a \:
push( #hashes, \%new_hash );
Otherwise you'll be putting the whole data structure in the array, etc.
In this line:
my %test = $hashes[0];
Your right-hand side is a scalar, a reference to a hash, hence you need to dereference to use that hash in the assignment:
my %test = %{$hashes[0]};
Messages like
HASH(0x2587a88)
Come from this line:
print "#hashes\n";
Not sure what you're intention is there, but you need to iterate over the data structure if you want to see the content (it'll be an array of references to hashes). You might consider using the Data::Dumper module to help you see the content there.

Related

Problem to get more than 1 element with same highest occurrence from array in Perl

I only get the smaller element as output although there are 2 elements with same highest occurrence in array
I have tried to remove sort function from the codes but it still returns me the smaller element
my(#a) = (undef,11,12,13,14,15,13,13,14,14);
my(%count);
foreach my $value (#a) {
$count{$value}++;
}
$max_value = (sort {$count{$b} <=> $count{$a}} #a)[0];
print "Max value = $max_value, occur $count{$max_value} times\n";
Expected result: Max value =13 14, occur 3 times
max_by from List::UtilsBy will return all values that share the maximum in list context.
use strict;
use warnings;
use List::UtilsBy 'max_by';
my #a = (undef,11,12,13,14,15,13,13,14,14);
my %count;
$count{$_}++ for #a;
my #max_values = max_by { $count{$_} } keys %count;
Your code simply takes the first maximal value it finds in the sorted data. You need to continue reading array elements until you reach one that is no longer maximal.
However, as you probably have to test all the hash values there's no great advantage to sorting it. You can just traverse it and keep track of the maximal value(s) you find.
my #a = (undef,11,12,13,14,15,13,13,14,14);
my %count;
$count{$_}++ for #a;
my ($max_count, #max_values);
while ( my ($k,$v) = each %count) {
if ($v > $max_count) {
#max_values = ($k);
$max_count = $v;
}
elsif ($v == $max_count) {
push #max_values, $k;
}
}
my $max_values = join " ", sort #max_values;
print "Max value = $max_values, occur $max_count times\n";
Note that undef is not a valid hash key - it gets converted to "".

Count Perl array size

I'm trying to print out the size of my array. I've followed a few other questions like this one on Stack Overflow. However, I never get the result I want.
All I wish for in this example is for the value of 3 to be printed as I have three indexes. All I get, from both print methods is 0.
my #arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
my $size = #arr;
print $size; # Prints 0
print scalar #arr; # Prints 0
What am I doing wrong, and how do I get the total size of an array when declared and populated this way?
First off:
my #arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
is nonsense. {} is for hash keys, so you are referring to %arr not #arr. use strict; and use warnings; would have told you this, and is just one tiny fragment of why they're considered mandatory.
To count the elements in an array, merely access it in a scalar context.
print scalar #arr;
if ( $num_elements < #arr ) { do_something(); }
But you would need to change your thing to
my #arr;
$arr[1] = 1;
$arr[2] = 2;
$arr[3] = 3;
And note - the first element of your array $arr[0] would be undefined.
$VAR1 = [
undef,
1,
2,
3
];
As a result, you would get a result of 4. To get the desired 'count of elements' you would need to filter the undefined items, with something like grep:
print scalar grep {defined} #arr;
This will take #arr filter it with grep (returning 3 elements) and then take the scalar value - count of elements, in this case 3.
But normally - you wouldn't do this. It's only necessary because you're trying to insert values into specific 'slots' in your array.
What you would do more commonly, is use either a direct assignment:
my #arr = ( 1, 2, 3 );
Or:
push ( #arr, 1 );
push ( #arr, 2 );
push ( #arr, 3 );
Which inserts the values at the end of the array. You would - if explicitly iterating - go from 0..$#arr but you rarely need to do this when you can do:
foreach my $element ( #arr ) {
print $element,"\n";
}
Or you can do it with a hash:
my %arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
This turns your array into a set of (unordered) key-value pairs, which you can access with keys %arr and do exactly the same:
print scalar keys %arr;
if ( $elements < keys %arr ) { do_something(); }
In this latter case, your hash will be:
$VAR1 = {
'1' => 1,
'3' => 3,
'2' => 2
};
I would suggest this is bad practice - if you have ordered values, the tool for the job is the array. If you have 'key' values, a hash is probably the tool for the job still - such as a 'request ID' or similar. You can typically tell the difference by looking at how you access the data, and whether there are any gaps (including from zero).
So to answer your question as asked:
my $size = #arr;
print $size; # prints 0
print scalar #arr; # prints 0
These don't work, because you never insert any values into #arr. But you do have a hash called %arr which you created implicitly. (And again - use strict; and use warnings; would have told you this).
You are initializing a hash, not an array.
To get the "size" of your hash you can write.
my $size = keys %arr;
I just thought there should be an illustration of your code run with USUW (use strict/use warnings) and what it adds to the troubleshooting process:
use strict;
use warnings;
my #arr;
...
And when you run it:
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 9.
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 10.
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 11.
Execution of - aborted due to compilation errors.
So USUW.
You may be thinking that you are instantiating an element of #arr when you are typing in the following code:
$arr{1} = 1;
However, you are instantiating a hash doing that. This tells me that you are not using strict or you would have an error. Instead, change to brackets, like this:
$arr[1] = 1;

Perl - How do I update (and access) an array stored in array stored in a hash?

Perhaps I have made this more complicated than I need it to be but I am currently trying to store an array that contains, among other things, an array inside a hash in Perl.
i.e. hash -> array -> array
use strict;
my %DEVICE_INFORMATION = {}; #global hash
sub someFunction() {
my $key = 'name';
my #storage = ();
#assume file was properly opened here for the foreach-loop
foreach my $line (<DATA>) {
if(conditional) {
my #ports = ();
$storage[0] = 'banana';
$storage[1] = \#ports;
$storage[2] = '0';
$DEVICE_INFORMATION{$key} = \#storage;
}
elsif(conditional) {
push #{$DEVICE_INFORMATION{$key}[1]}, 5;
}
}#end foreach
} #end someFunction
This is a simplified version of the code I am writing. I have a subroutine that I call in the main. It parses a very specifically designed file. That file guarantees that the if statement fires before subsequent elsif statement.
I think the push call in the elsif statement is not working properly - i.e. 5 is not being stored in the #ports array that should exist in the #storage array that should be returned when I hash the key into DEVICE_INFORMATION.
In the main I try and print out each element of the #storage array to check that things are running smoothly.
#main execution
&someFunction();
print $DEVICE_INFORMATION{'name'}[0];
print $DEVICE_INFORMATION{'name'}[1];
print $DEVICE_INFORMATION{'name'}[2];
The output for this ends up being... banana ARRAY(blahblah) 0
If I change the print statement for the middle call to:
print #{$DEVICE_INFORMATION{'name'}[1]};
Or to:
print #{$DEVICE_INFORMATION{'name'}[1]}[0];
The output changes to banana [blankspace] 0
Please advise on how I can properly update the #ports array while it is stored inside the #storage array that has been hash'd into DEVICE_INFORMATION and then how I can access the elements of #ports. Many thanks!
P.S. I apologize for the length of this post. It is my first question on stackoverflow.
I was going to tell you that Data::Dumper can help you sort out Perl data structures, but Data::Dumper can also tell you about your first problem:
Here's what happens when you sign open-curly + close-curly ( '{}' ) to a hash:
use Data::Dumper ();
my %DEVICE_INFORMATION = {}; #global hash
print Dumper->Dump( [ \%DEVICE_INFORMATION ], [ '*DEVICE_INFORMATION ' ] );
Here's the output:
%DEVICE_INFORMATION = (
'HASH(0x3edd2c)' => undef
);
What you did is you assigned the stringified hash reference as a key to the list element that comes after it. implied
my %DEVICE_INFORMATION = {} => ();
So Perl assigned it a value of undef.
When you assign to a hash, you assign a list. A literal empty hash is not a list, it's a hash reference. What you wanted to do for an empty hash--and what is totally unnecessary--is this:
my %DEVICE_INFORMATION = ();
And that's unnecessary because it is exactly the same thing as:
my %DEVICE_INFORMATION;
You're declaring a hash, and that statement fully identifies it as a hash. And Perl is not going to guess what you want in it, so it's an empty hash from the get-go.
Finally, my advice on using Data::Dumper. If you started your hash off right, and did the following:
my %DEVICE_INFORMATION; # = {}; #global hash
my #ports = ( 1, 2, 3 );
# notice that I just skipped the interim structure of #storage
# and assigned it as a literal
# * Perl has one of the best literal data structure languages out there.
$DEVICE_INFORMATION{name} = [ 'banana', \#ports, '0' ];
print Data::Dumper->Dump(
[ \%DEVICE_INFORMATION ]
, [ '*DEVICE_INFORMATION' ]
);
What you see is:
%DEVICE_INFORMATION = (
'name' => [
'banana',
[
1,
2,
3
],
'0'
]
);
So, you can better see how it's all getting stored, and what levels you have to deference and how to get the information you want out of it.
By the way, Data::Dumper delivers 100% runnable Perl code, and shows you how you can specify the same structure as a literal. One caveat, you would have to declare the variable first, using strict (which you should always use anyway).
You update #ports properly.
Your print statement accesses $storage[1] (reference to #ports) in wrong way.
You may use syntax you have used in push.
print $DEVICE_INFORMATION{'name'}[0], ";",
join( ':', #{$DEVICE_INFORMATION{'name'}[1]}), ";",
$DEVICE_INFORMATION{'name'}[2], "\n";
print "Number of ports: ", scalar(#{$DEVICE_INFORMATION{'name'}[1]})),"\n";
print "First port: ", $DEVICE_INFORMATION{'name'}[1][0]//'', "\n";
# X//'' -> X or '' if X is undef

Pushing an array reference repeatedly: the pushed references end up all equal

I want to create a 2d array(#combinations) that holds all combinations of another array (#indices).
I'm using push to append a reference to another array(#temp2). When I print my 2d array (using Dumper) it is not as I expect: the print statement inside the loop shows that every pushed reference is to a non-empty list, but eventually all my references point to an empty list. Why?
use Math::Combinatorics;
use Data::Dumper;
my (#combinations, #temp2);
my #indices = (0, 2, 4);
my $count = 1;
my $counter = #indices;
while ($counter>= $count) {
my $c = Math::Combinatorics->new(
count => $count,
data => \#indices,
);
$count++;
while (#temp2 = $c->next_combination) {
print "#temp2 \n";
push #combinations, \#temp2;
}
}
print Dumper(\#combinations);
Because you declare #temp2 at the top level, the reference \#temp2 will always point to the same data. Because you exit the loop as soon as #temp2 is empty, all the references in #combinations will point to this same empty array.
The remedy is easy: declare the #temp2 to be local to the while loop, by writing
while (my #temp2 = $c->next_combination) {
This will create a new variable #temp2, with its own reference, each time the loop is repeated.

Array context assignment to empty list reference in Perl-- please explain the line " my #arr = [ ]; "

I'm new to Perl. I understand that my #a = (); is equivalent to my #a;-- both initialize an empty array. Similarly, my $a = []; initializes an empty array that's referenced by $a.
However, I'm perplexed by my #arr = []; which is also legal Perl. According to ref() and print, #arr is an array, not a reference to one. It can be pushed onto, which seems to return the number of elements in the array. But it seems to contain a reference to another array, which can also be pushed onto:
#!/usr/bin/perl
use v5.16;
my #arr = [];
push(#arr, 1);
print join(", ", #arr) . "\n";
push(#arr[0], 11);
push(#arr[0], 12);
print "a[0]: " . join(", ", #{#arr[0]}) . "\n";
Outputs
ARRAY(0xd6ede8), 1
a[0]: 11, 12
What is going on here? Detail is greatly appreciated.
#arr = [];
is the same as
#arr = ([]);
or
#arr = (); $arr[0] = [];
It creates an array with one element, a reference to an empty array.
The statements
#arr = [];
push #arr, 1;
create an array with two elements: a reference to an empty array, and the scalar value 1, like saying
#arr = ();
$arr[0] = [];
$arr[1] = 1;
And finally, in the statements
push(#arr[0], 11);
push(#arr[0], 12);
the first argument is equivalent to $arr[0], an array reference in this example. Since Perl >=v5.14, the push function can operate on an array reference as well as an array, so these statements are adding elements to the (initially empty) array reference held in $arr[0]. So now the contents of #arr are equivalent what you'd get if you said
#arr = ();
$arr[0] = [ 11, 12 ]; # ref to array with two elements
$arr[1] = 1;
Your understanding is correct.
The [] always creates an array reference, and references always are scalars. Here, my #arr = [] (or equivalently: my #arr = ([])) creates an array #arr with the first element being an array reference.
Scalars are never context-dependent! Context only matters for
hash or array variables: #foo and %foo
subroutine calls, as a call propagates the current context and can be queried via wantarray. Subroutines may therefore behave differently in various context.
operators, as some operators like , or .. are entirely different depending on context.
[] is a scalar value, which happens to be an array reference.
This:
my #arr = 42;
creates #arr as an array with a single element; that element's value is 42. It's more clearly written as:
my #arr = ( 42 );
Very similarly, this:
my #arr = [];
also creates #arr as an array with a single element; that element's value is [], an array reference. It's more clearly written as:
my #arr = ( [] );
which means the same thing.
Or, if you really wanted #arr to be an empty array:
my #arr = ();
or possibly something like:
my $arr_ref = [];
(It is perhaps unfortunate that a scalar value in array context is treated as a single-element list; I suspect that the confusion it causes exceeds the usefulness of the feature.)

Resources