Accessing array of hash in Perl - arrays

I have problem with accessing a hash in each element of array after creating it but it gave the last element. What should I do to access all the elements of my array?
#stem = ();
for($i=0;$i<2;++$i){
push #stem,{u1=>1 , u2 => 2 , u3 => 3};
}
#ants = ();
$count = 0;
for($i=0;$i<scalar(#stem);++$i){
#allowed = ();
%hash = ();
for($j=0;$j<scalar(#stem);++$j){
push #allowed,{stem=>++$count,hinfo=>++$count};
}
%hash = ( allowed=>\#allowed ,solution=>++$count);
push (#ants,\%hash);
}
for($i=0;$i<scalar(#ants);++$i){
%test = %{$ants[$i]};
print "=>",$test{solution},"\n";
#temp = #{$test{allowed}};
for($j=0;$j<scalar(#temp);++$j){
print $j,":",$temp[$j]->{stem}," ",$temp[$j]->{hinfo},"\n";
}
}
output:
=>21
0:16 16
1:18 18
2:20 20
=>21
0:16 16
1:18 18
2:20 20

Since there is just one variable #allowed and one variable %hash, where you take references to them you are always getting the same value (which was cleared and set in the final iteration of the loop).
Declare them inside the loop, and you will be get references to new variables each time through the loop:
for($i=0;$i<scalar(#stem);++$i){
my #allowed;
my %hash;
for($j=0;$j<scalar(#stem);++$j){
push #allowed,{stem=>++$count,hinfo=>++$count};
}
%hash = ( allowed=>\#allowed ,solution=>++$count);
push (#ants,\%hash);
}

Related

Why can't I push items into a hash of arrays, in Perl?

What I want here is for #{$allHash{$key1}} to be ["item1", "item2"].
Pushing doesn't work, and all the print statements are to try and find where the pushed items went.
Using -> notation in the push is even worse, it makes $temp[0][0] and line 24 show an array instead of the item.
#use strict; # I've turned these off here to make the code easier to read
#use warnings;
my %allHash = ();
my $key1 = "key1";
my $item1 = "item1";
my $item2 = "item2";
#if (!exists($allHash{key1})) {$allHash{key1}=();}; # makes no difference, the array autovivefies anyway
push (#{$allHash{$key1}}, $item1); # push 1st item
print"\n\nat11: pushed $key1, $item1";
my #temp = $allHash{$key1};
print"\nat13:temp=#temp, length=",0+#temp, ", temp[0]=$temp[0], temp[0][0]=$temp[0][0]";
print"\nat14: allHash{$key1}[0]= $allHash{$key1}[0]";
print"\nat15: allHash{$key1}[1]= $allHash{$key1}[1]";
print"\nat16: allHash{$key1}[0][0]= $allHash{$key1}[0][0]";
print"\nat17: allHash{$key1}[1][0]= $allHash{$key1}[1][0]";
print"\nat18: allHash{$key1}[0][1]= $allHash{$key1}[0][1]\n";
print"\n----------------";
push (#{$allHash{$key1}}, $item2); # push 2d item
print"\n\nat21: pushed $key1, $item2";
#temp = #{allHash{$key1}};
print"\nat23:temp=#temp, length=",0+#temp, ", temp[0]=$temp[0], temp[0][0] =$temp[0][0]";
print"\nat24: allHash{$key1}[0]= $allHash{$key1}[0]";
print"\nat25: allHash{$key1}[1]= $allHash{$key1}[1], allHash{$key1}[1][0] =$allHash{$key1}[1][0]";
print"\nat26: allHash{$key1}[0][0]= $allHash{$key1}[0][0]";
print"\nat27: allHash{$key1}[1][0]= $allHash{$key1}[1][0]";
print"\nat28: allHash{$key1}[0][1]= $allHash{$key1}[0][1]\n";
The output from the above program is:
at11: pushed key1, item1
at13:temp=, length=1, temp[0]=ARRAY(0x331eb8), temp[0][0]=item1
at14: allHash{key1}[0]=item1
at15: allHash{key1}[1]=
at16: allHash{key1}[0][0]=
at17: allHash{key1}[1][0]=
at18: allHash{key1}[0][1]=
----------------
at21: pushed key1, item2
at23:temp=ARRAY(0x331ee8), length=1, temp[0]=ARRAY(0x331ee8), temp[0][0]=item1
at24: allHash{key1}[0]=item1
at25: allHash{key1}[1]= ARRAY(0x332020), allHash{key1}[1][0]=
at26: allHash{key1}[0][0]=
at27: allHash{key1}[1][0]=
at28: allHash{key1}[0][1]=
What's bizarre is that this almost identical code from another of my programs works perfectly.
%hedgeHash = (); # collect the members of each hedge as an array, using stub as key
for (my $i=0; $i<#options; $i++)
{ $Hstub = $options[$i][$iStub];
push #{$hedgeHash{$Hstub}}, $i; # hedgehash should contain array of members of the hedge.
}
What's even more bizarre is that if I remove the parentheses from the push statement, I no longer get 'item1' as the output of #temp and on lines 14 and 24, but get another array! WTF??
Please see a sample code bellow demonstrating a use of hash of arrays.
Indeed OP's coding style makes code reading somewhat difficult.
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my %allHash;
my $key1 = 'key1';
my $item1 = 'item1';
my $item2 = 'item2';
my $item3 = 'item3';
my $item4 = 'item4';
my $item5 = 'item5';
push #{$allHash{$key1}}, $item1;
push #{$allHash{$key1}}, $item2;
$allHash{$key1}[2] = $item3;
$allHash{$key1}[3] = [$item4,$item5];
say Dumper(\%allHash);
Output
$VAR1 = {
'key1' => [
'item1',
'item2',
'item3',
[
'item4',
'item5'
]
]
};

Add an array to a hash entry that is already storing an array

I need to evaluate a series of strings that I want to convert into a hash of arrays. Take into account that, in this case, I want to add an array to an entry in a hash that it is already storing an array. I need to get the following hash:
ConfigurationHash{'Editor'} = (John, Mary, Jane, Peter)
I have stripped down my code to this example:
use strict;
use warnings;
my %ConfigurationHash;
my $String1 = "Editor=John,Mary";
my $String2 = "Editor=Jane,Peter";
my #Line1 = split ("=", $String1);
my #Line2 = split ("=", $String2);
my $Variable1 = #Line1[0];
my $Value1 = #Line1[1];
my $Variable2 = #Line2[0];
my $Value2 = #Line2[1];
my #Values1Array = split(",", $Value1);
my #Values2Array = split(",", $Value2);
if ( ! exists $ConfigurationHash{$Variable1} ) {
$ConfigurationHash{$Variable1} = #Values1Array;
}
else {
push (#ConfigurationHash{$Variable1}, #Values1Array);
}
which produces the following error:
Experimental push on scalar is now forbidden at ./test.pl line 25, near "#Values1Array)"
Execution of ./test.pl aborted due to compilation errors.
I know that the problem lies in references/dereferences, but my knowledge of perl is so basic that I'm not able to figure how to get there by myself.
Could anybody show me how to do it? I would also appreciate if you could show me how to iterate the values of the array in the hash once it is created.
It's unclear why you have $String2 and its derivatives in your code as they are never used. This code processes both strings
You simply need to push the list of values to the array corresponding to $Variable1 (dreadful choice of identifier) in the hash. Accomplish this by dereferencing the array element
use strict;
use warnings;
my %config;
my $s1 = 'Editor=John,Mary';
my $s2 = 'Editor=Jane,Peter';
for ( $s1, $s2 ) {
my ($key, #values) = split /[=,]/;
push #{ $config{$key} }, #values;
}
use Data::Dumper;
print Dumper \%config;
output
$VAR1 = {
'Editor' => [
'John',
'Mary',
'Jane',
'Peter'
]
};
This line isn't doing what you think it does.
$ConfigurationHash{$Variable1} = #Values1Array;
If you printed out what $ConfigurationHash{$Variable1} contains you'll find it only contains the size of #Values1Array.
You should be fine to use push but with a slight modification to what you've written.
push #{$ConfigurationHash{$Variable1}}, #Values1Array;
I've also removed the brackets as you don't need them.
As for iterating over the array, it is no different to iterating over a regular array. You were likely having problems iterating over it before as you didn't have an array
foreach my $whatever (#{$ConfigurationHash{$Variable1}})
{
# Code
}
Thank you to all who posted answers. #Borodin, you're right, I missed a second block that used $String2 and its derivatives, but I think it was obvious it was at the end and was similar to the if-else block in my original code.
Thank you, #chris-turner, for giving me the hint on how to use push the right way and pointing out the error in the $ConfigurationHash{$Variable1} = #Values1Array;
With all these contributions I figured out that the right code I was expecting is:
use strict;
use warnings;
my %ConfigurationHash;
my $String1 = "Editor=John,Mary";
my $String2 = "Editor=Jane,Peter";
my #Line1 = split ("=", $String1);
my #Line2 = split ("=", $String2);
my $Variable1 = $Line1[0];
my $Value1 = $Line1[1];
my $Variable2 = $Line2[0];
my $Value2 = $Line2[1];
my #Values1Array = split(",", $Value1);
my #Values2Array = split(",", $Value2);
if ( ! exists $ConfigurationHash{$Variable1} ) {
$ConfigurationHash{$Variable1} = \#Values1Array;
}
else {
#push (#ConfigurationHash{$Variable1}, #Values1Array);
push #{$ConfigurationHash{$Variable1}}, #Values1Array;
}
if ( ! exists $ConfigurationHash{$Variable2} ) {
$ConfigurationHash{$Variable2} = \#Values2Array;
}
else {
#push (#ConfigurationHash{$Variable2}, #Values2Array);
push #{$ConfigurationHash{$Variable2}}, #Values2Array;
}
use Data::Dumper;
print Dumper \%ConfigurationHash;
Which outputs the following:
$VAR1 = {
'Editor' => [
'John',
'Mary',
'Jane',
'Peter'
]
};

Use of uninitialized values in a list

I load some variables from the URL and want to check in my CGI script whether are they initialized. I quickly found out that I can not put uninitialized values into an list in Perl.
My approach was simply to iterate through the list and use a switch variable $sucessDOM (1 if all data had been initialized and 0 elsewhere).
I came up with this:
### Read data from HTML
my $mailinglist = param( 'mailinglist' );
my $firstname = param( 'firstname' );
my $lastname = param( 'lastname' );
my $mail = param( 'mail' );
my $sucessDOM = 0;
my #DOM = { $mailinglist, $firstname, $lastname, $mail };
### Data validation
for my $data ( #DOM ) {
if ( $data eq undef ) {
$sucessDOM = $sucessDOM + 1;
}
if ( $sucessDOM = #DOM ) {
print "#DOM\n";
$sucessDOM = 1;
}
else {
$sucessDOM = 0;
}
}
The output is as follows
[Fri Dec 22 20:30:40 2017] read_form.cgi: Use of uninitialized value $mailinglist in anonymous hash ({}) at ./read_form.cgi line 20.
[Fri Dec 22 20:30:40 2017] read_form.cgi: Use of uninitialized value $lastname in anonymous hash ({}) at ./read_form.cgi line 20.
[Fri Dec 22 20:30:40 2017] read_form.cgi: Use of uninitialized value in string eq at ./read_form.cgi line 24.
HASH(0x55c8a73eeea8)
I also cannot understand why are there errors for only two values in the array if they are all uninitialized, as I run the script directly without passing any values.
You have at least four problems with your code:
First, {} makes an anonymous hash and returns a reference to it; this is a single value that ends up in #DOM. The warnings are because hash keys have to be strings, and warn if they are undef. You should be assigning like
my #DOM = ( $mailinglist, $firstname, $lastname, $mail );
Second, eq compares strings, so it would convert undef to ''. To test if $data is undef, do
if (! defined $data) {
Third, you are checking if all the array elements were successful inside the for loop (where that will not be true on any but the last iteration of the loop). Move the if ($successDOM... block to after the for loop.
Fourth, = is just an assignment operator; use == for numeric comparison:
if ($successDOM == #DOM) {

Perl, grouping Array of Array element based on one column and condition

I have an AoA construct with four columns and many rows. Following is an example of data (input).
DQ556929 103480190 103480214 154943
DQ540839 103325247 103325275 2484
DQ566549 103322763 103322792 99
DQ699634 103322664 103322694 0
DQ544472 103322664 103322692 373
DQ709105 103322291 103322318 46
DQ705937 103322245 103322273 486
DQ699398 103321759 103321788 1211
DQ710151 103320548 103320577 692251
DQ548430 102628297 102628326 1
DQ558403 102628296 102628321 855795
DQ692476 101772501 101772529 481463
DQ544274 101291038 101291068 484047
DQ723982 100806991 100807020 1
DQ709023 100806990 100807020 3
DQ712307 100806987 100807014 0
DQ709654 100806987 100807012 571051
DQ707370 100235936 100235962 1481849
I want to group and write into a file all the row elements (sequentially).
Conditions are if column four values less than 1000 and minimum two values are next to each other, group them else if the value less than 1000 and lies between the values more than 1000 treat them as single and append separately in the same file and the values which are more than 1000 also write as a block but with out affecting the order of the 2nd and third column.
This file is output of my previous program, now for this I have tried implementing my hands but getting some weird results. Here is my chunk of code, but non functional. Guys I need just help if i am executing my logic well here, I am open for any comments as a beginner. And also correct me anywhere.
my #dataf= sort{ $a->[1]<=> $b->[1]} #data;
#dataf=reverse #dataf;
for(my $i>=0;$i<=$#Start;$i++)
{
print "$sortStart[$i]\n";
my $diff = $sortStart[$i] - $sortStart[$i+1];
$dataf[$i][3]= $diff;
# $IDdiff{$ID[$i]}=$diff;
}
#print Dumper(#dataf);
open (CLUST, ">> ./clustTest.txt" );
for (my $k=0;$k<=$#Start;$k++)
{
for (my $l=0;$l<=3;$l++)
{
# my $tempdataf = shift $dataf[$k][$l];
# print $tempdataf;
if ($dataf[$k][3]<=1000)
{
$flag = 1;
do
{
print CLUST"----- Cluster $clustNo -----\n";
print CLUST"$dataf[$k][$l]\t";
if ($dataf[$k][3]<=1000)
{
$flag1 = 1;
}else {$flag1=0;}
$clustNo++;
}until($flag1==0 && $data[$k][3] > 1000);
if($flag1==0 && $data[$k][3] > 1000)
{
print CLUST"Singlet \n";
print CLUST"$dataf[$k][$l]\t";
next;
}
#print CLUST"$dataf[$k][$l]\t"; ##IDdiff
}
print CLUST"\n";
}
}
Expected output in file:
Singlets
DQ556929 103480190 103480214 154943
DQ540839 103325247 103325275 2484
Cluster1
DQ566549 103322763 103322792 99
DQ699634 103322664 103322694 0
DQ544472 103322664 103322692 373
DQ709105 103322291 103322318 46
DQ705937 103322245 103322273 486
Singlets
DQ699398 103321759 103321788 1211
DQ710151 103320548 103320577 692251
DQ548430 102628297 102628326 1
DQ558403 102628296 102628321 855795
DQ692476 101772501 101772529 481463
DQ544274 101291038 101291068 484047
Cluster2
DQ723982 100806991 100807020 1
DQ709023 100806990 100807020 3
DQ712307 100806987 100807014 0
Singlets
DQ709654 100806987 100807012 571051
DQ707370 100235936 100235962 1481849
This seems to produce the expected output. I'm not sure I understood the specification correctly, so there might be errors and edge cases.
How it works: it remembers what kind of section it's currently outputting ($section, Singlet or Cluster). It accumulates lines in the #cluster array if they belong together, when an incompatible line arrives, the cluster is printed and a new one is started. If the cluster to print has only one member, it's treated as a singlet.
#!/usr/bin/perl
use warnings;
use strict;
my $section = q();
my #cluster;
my $cluster_count = 1;
sub output {
if (#cluster > 1) {
print "Cluster$cluster_count\n";
$cluster_count++;
} elsif (1 == #cluster) {
print $section = 'Singlet', "s\n" unless 'Singlet' eq $section;
}
print for #cluster;
#cluster = ();
}
my $last = 'INF';
while (<>) {
my ($id, $from, $to, $value) = split;
if ($value > 1000 || 1000 < abs($last - $from)) {
output();
} else {
$section = 'Cluster';
}
push #cluster, $_;
$last = $to;
}
output();

Derefference Date

I have a little script where I want to return an array of Dates between two dates.
Problem is that the scalar that is being added is by reference, how do I store a copy or the derefferenced value
#!/usr/bin/perl
use strict;
use warnings;
use DateTime;
my $now = DateTime->today;
my $start_date = DateTime->today;
$start_date = $start_date->subtract( days => 45 );
my #dates;
while ( $start_date <= $now ) {
push #dates, $start_date;
$start_date->add( days => 1 );
}
my $date;
foreach (#dates) {
print $_->ymd('/'), "\n";
}
You can clone the object as you push it onto the array, like this
my #dates;
while ( $start_date <= $now) {
push #dates, $start_date->clone;
$start_date->add( days => 1 );
}
foreach (#dates) {
print $_->ymd('/'), "\n";
}
but that is wasteful if you want only ever want the YMD string from each date. You can just push that instead
my #dates;
while ( $start_date <= $now) {
push #dates, $start_date->ymd('/');
$start_date->add( days => 1 );
}
print "$_\n" for #dates;
You can set up the array more simply by working on the elements of the array itself, as follows
my #dates = (DateTime->today);
unshift #dates, $dates[0]->clone->subtract(days => 1) for 1 .. 45;
But in the end it is neater, and probably faster, to use the Time::Piece to do the same thing. It is a core module, and so shouldn't need installing if your copy of perl is at all recent, it is far smaller than DateTime, and is probably faster
use strict;
use warnings;
use Time::Piece;
use Time::Seconds 'ONE_DAY';
my #dates = map { localtime() - $_ * ONE_DAY } reverse 0 .. 45;
print $_->ymd('/'), "\n" for #dates;
output
2014/07/24
2014/07/25
2014/07/26
2014/07/27
2014/07/28
2014/07/29
2014/07/30
2014/07/31
2014/08/01
2014/08/02
2014/08/03
2014/08/04
2014/08/05
2014/08/06
2014/08/07
2014/08/08
2014/08/09
2014/08/10
2014/08/11
2014/08/12
2014/08/13
2014/08/14
2014/08/15
2014/08/16
2014/08/17
2014/08/18
2014/08/19
2014/08/20
2014/08/21
2014/08/22
2014/08/23
2014/08/24
2014/08/25
2014/08/26
2014/08/27
2014/08/28
2014/08/29
2014/08/30
2014/08/31
2014/09/01
2014/09/02
2014/09/03
2014/09/04
2014/09/05
2014/09/06
2014/09/07
Update
To store strings in the array instead of Time::Piece objects, you could write this instead
use strict;
use warnings;
use Time::Piece;
use Time::Seconds 'ONE_DAY';
my $today = localtime;
my #dates = map { ($today - $_ * ONE_DAY)->ymd('/') } reverse 0 .. 45;
print "$_\n" for #dates;
The output is identical to that of the previous program.
apparently there is a function for it called clone()
so this
push(#dates, $start_date);
changes into
push(#dates, $start_date->clone);

Resources