i have a problem where I need to have an array as a value in an associative array.
Go through the code below. Here I am trying to loop the files in a directory and it is more likely that more than 1 file can have the same ctrno. So, I would like to see what are all the files having the same ctrno. The code below gives error at "$ctrno_hash[$ctrno] = #arr;" in the else condition. The same case would be for if condition as well.
Am I following the right approach or could it be done differently?
sub loop_through_files
{
$file = "#_";
open(INPFILE, "$file") or die $!;
#print "$file:$ctrno\n";
while (<INPFILE>)
{
$line .= $_;
}
if ($line =~ /$ctrno/ )
{
print "found\n";
if ( exists $ctrno_hash[$ctrno])
{
local #arr = $ctrno_hash[$ctrno];
push (#arr, $file);
$ctrno_hash[$ctrno] = #arr;
}
else
{
local #arr;
push(#arr, $file);
$ctrno_hash[$ctrno] = #arr;
}
}
}
I believe you want something like
$ctrno_hash[$ctrno] = \#arr;
This will turn the array #arr into a array reference.
You then refer to the previously pushed array reference with
#{$ctrno_hash[$ctrno]}
That is, if $array_ref is an array reference, the construct #{ $array_ref } returns the array to which the array reference points.
Now, the construct $ctrno_hash[$ctrno] is not really a hash, but an ordinary array. In order to truly make it a hash, you need the curly brackets instead of the square brackets:
#{$ctrno_hash{$ctrno} } = \#arr;
And similarly, you later refer to the array with
#{$ctrno_hash{$ctrno} }
Now, having said that, you can entirly forgo the if ... exists construct:
if ($line =~ /$ctrno/ )
{
print "found\n";
push #{$ctrno_hash{$ctrno}}, $file
}
Related
I have the code bellow. I want to assign for each version values but somehow I do not know what I'm doing wrong.
%buildsMap = ();
#read schedule
opendir (DIR, $buildsDir) or die $!;
while ( my $file = readdir DIR ) {
if (($file =~ /(V\d\d\d.*)-DVD/) || ($file =~ /(V\d\d\d.*)/)) {
foreach $version(#versionS){
if ($1 eq $version ){
#temp = #{$buildsMap{$version}};
push #temp,$file;
#{$buildsMap{$version}} = #temp;
}
}
}
}
If I want to use the keys from this hashmap it is ok. Please advice me.
First order of business, turn on strict and warnings. This will discover any typo'd variables and other mistakes. The major issue here will be you have to declare all your variables. Fixing that all up, and printing out the resulting %buildMap, we have a minimum viable example.
use strict;
use warnings;
use v5.10;
my $buildsDir = shift;
my #versionS = qw(V111 V222 V333);
my %buildsMap = ();
#read schedule
opendir (DIR, $buildsDir) or die $!;
while ( my $file = readdir DIR ) {
if (($file =~ /(V\d\d\d.*)-DVD/) || ($file =~ /(V\d\d\d.*)/)) {
foreach my $version (#versionS) {
if ($1 eq $version ){
my #temp = #{$buildsMap{$version}};
push #temp,$file;
#{$buildsMap{$version}} = #temp;
}
}
}
}
for my $version (keys %buildsMap) {
my $files = $buildsMap{$version};
say "$version #$files";
}
Which gives the error Can't use an undefined value as an ARRAY reference at test.plx line 15.. That's this line.
my #temp = #{$buildsMap{$version}};
The problem here is how you're working with array references. That specific line fails because if $buildsMap{$version} has no entry you're trying to dereference nothing and Perl won't allow that, not for a normal dereference. We could fix that, but there's better ways to work with hashes of lists. Here's what you have.
my #temp = #{$buildsMap{$version}};
push #temp,$file;
#{$buildsMap{$version}} = #temp;
That copies out all the filenames into #temp, works with #temp and the more comfortable syntax, and copies them back in. It's inefficient for memory and amount of code. Instead, we can do it in place. First by initializing the value to an empty array reference, if necessary, and then by pushing the file onto that array reference directly.
$buildsMap{$version} ||= [];
push #{$buildsMap{$version}}, $file;
||= is the or-equals operator. It will only do the assignment if the left-hand side is false. It's often used to set default values. You could also write $buildsMap{$version} = [] if !$buildsMap{$version} but that rapidly gets redundant.
But we don't even need to do that! push is a special case. For convenience, you can dereference an empty value and pass it to push! So we don't need the initializer, just push.
push #{$buildsMap{$version}}, $file;
While the code works, it could be made more efficient. Instead of scanning #versionS for every filename, potentially wasteful if there's a lot of files or a lot of versions, you can use a hash. Now there's no inner loop needed.
my %versionS = ( 'V111' => 1, 'V222' => 2, 'V333' => 3 );
...
if (($file =~ /(V\d\d\d.*)-DVD/) || ($file =~ /(V\d\d\d.*)/)) {
my $version = $1;
if ($versionS{$version}){
push #{$buildsMap{$version}}, $file;
}
}
I am trying to create a hash of array. Currently the array is of size 1 only.
$f1="/scratch/aime1/PerlCode/BeforeUpgrade.csv";
$f2="/scratch/aime1/PerlCode/AfterUpgrade.csv";
open(FIN, "< $f1");
while( $my_line = <FIN> )
{
chomp $my_line;
my #values = split(',', $my_line);
my $key = shift #values;
print "$key\n";
print "#values\n";
$hash1{$key} = #values;
}
close(FIN);
for (keys %hash1) {
my #value_array = $hash1{$_};
print "Key is $_ and first Element of array is $value_array[0] \n";
}
So,the key is of the form /scratch/aime1/idmUpgrade/idmUpgrade and the value is its permission i.e. 0755
When I try to print this hash,output is:
Key is /scratch/aime1/idmUpgrade/idmUpgrade and first Element of array is 1
Array is always printed as 1 and not as 0755.
Always include use strict; and use warnings; at the top of EVERY perl script.
You're assigning an array to a hash key, to do that you need to take a reference. Otherwise, you're just assigning the array count (which is 1)
$hash1{$key} = \#values;
Similarly, when you want to retrieve, it, you'll need to dereference it:
my #value_array = #{$hash1{$_}};
I am having a very small difficulty in Perl.
I am reading a text file which for some information. While I read the text file, I select some keys from text; as I read further, I wish to save an array of values for the keys.
See for e.g.
Alpha 1
Something: 2132
Something: 2134
Alpha 2
Something: 2132
Something: 2134
I read the file into an array called lines:
my $h;
my $alpha;
for my $line (#lines){
if ($line =~ m/Alpha (\d+)/){
$alpha = $1;
$h->{$alpha} = (); # create empty array for key?
}
elsif ($line =~ m/Something: (\d+)/){
push($h->{$alpha}, $1);
}
}
Apparently, it gives me an error:
Type of arg 1 to push must be array (not hash element) at test.pl line 28, near "$1)"
Execution of test.pl aborted due to compilation errors.
Unable to figure this out.
A hash key value can contain only a scalar. If you want to store an array, then you need to go for array reference.
You can do something like this:
for my $line (#lines){
if ($line =~ m/Alpha (\d+)/){
$alpha = $1;
$h->{$alpha} = []; # create empty array refernece for key
}
elsif ($line =~ m/Something: (\d+)/){
push( #{$h->{$alpha}}, $1);
}
}
You need to make two changes:
$h->{$alpha} = [];
** - Create an anonymous array and store
a ref to it in the hash
And
push(#{$h->{$alpha}}, $1);
because push requires an actual array, and you have an array reference. The #{...} wrapper dereferences the arrayref to an actual array.
As earlier answers say, they are correct. But not perfect. $alpha may remain undef. Hence in order to avoid it please add a check.
my $alpha;
for my $line (#lines){
if ($line =~ m/Alpha (\d+)/){
$alpha = $1;
$h->{$alpha} = []; # create empty array refernece for key
}
elsif ($line =~ m/Something: (\d+)/){
if(defined $apha) { ## Check
push( #{$h->{$alpha}}, $1);
}
}
}
I'm reading a file. I want a hash that gives me the first number of a line as a key to a hash of all the numbers of the rest of the line to 1.
I believe I'm adding the hash correctly, because Dumper prints correctly.
However, print "$first $secondID\n" is not giving me any output.
while (<FILE>) {
chomp $_;
if (/(\d+)\t(.+)/) {
$firstNum = $1;
#seconds = split(/\,/,$2);
foreach $following (#seconds) {
$Pairs->{$firstNum}{$following} = 1;
}
foreach $first (sort {$a <=> $b} keys %Pairs) {
print "$first\n";
%second = {$Pairs{$first}};
foreach $secondID (sort {$a <=> $b} keys %second) {
print "$first $secondID\n";
}
}
print Dumper($Pairs);
}
else {
print "ERROR\n";
}
}
Later on, given a pair of numbers I would like to look up to see whether $Pairs{$num1}{$num2} is defined. would I write
if(defined $Pairs{$num1}{$num2})
Or should I check the first key first. Then check the second key
if (defined $Pairs{$num1}) {
$temp = $Pairs{$num1};
if (defined $temp{$num2}) {
print "true\n;
}
}
You have a couple of errors. Firstly you seem to be unsure whether you are using %Pairs or $Pairs to store your hash, and secondly you have %second = {$Pairs{$first}}, which tries to assign a hash reference to the hash %second. Presumably you want my %second = %{ $Pairs{$first} }.
You should always use strict and use warnings at the start of all your Perl programs, and declare all variables at the point of first use using my. This will alert you to simple mistakes you could otherwise easily overlook, and would have shown up your use of both %Pairs and $Pairs in this program, as well as your attempt to assign a single value (a hash reference) to a hash.
Rather than copying the entire hash, you should save a reference to it in $seconds. Then you can dereference it in the following for loop.
Experienced Perl programmers would also thank you for using lower-case plus underscore for local (my) variables, and reserving capitals for package and class names.
This program works as you intended, and expects the file name as a command-line parameter:
use strict;
use warnings;
my %pairs;
while (<>) {
unless ( /(\d+)\s+(.+)/ ) {
print "ERROR\n";
next;
}
my $first_num = $1;
my #seconds = split /,/, $2;
foreach my $following (#seconds) {
$pairs{$first_num}{$following} = 1;
}
foreach my $first (sort { $a <=> $b } keys %pairs) {
print "$first\n";
my $second = $pairs{$first};
foreach my $second_id (sort { $a <=> $b } keys %$second) {
print "$first $second_id\n";
}
}
}
my %hash;
while ( <> ) {
my #numbers = split /\D+/;
my $key = shift #numbers;
#{$hash{$key}}{ #numbers } = ( 1 ) x #numbers;
}
# test it this way...
if ( $hash{ $num1 }{ $num2 } ) {
}
Use:
%second = %{$Pairs->{$first}};
I have a data in an array as below. I want to copy all the content in a single variable. How can I do this ?
IFLADK
FJ
FAILED
FNKS
FKJ
FAILED
You could assign a reference to the array
my $scalar = \#array;
… or join all the strings in the array together
my $scalar = join "\n", #array;
With reference to previous question How to read n lines above the matched string in perl? Storing multiple hits in an array:
while (<$fh>) {
push #array, $_;
shift #array if #array > 4;
if (/script/) {
print #array;
push #found, join "", #array; # <----- this line
}
}
You could just use a scalar, e.g. $found = join "", #array, but then you would only store the last match in the loop.
Suppose the loop is finished, and now you have all the matches in array #found. If you want them in a scalar, just join again:
my $found = join "", #found;
Or you can just add them all at once in the loop:
$found .= join "", #array;
It all depends on what you intend to do with the data. Having the data in a scalar is rarely more beneficial than having it in an array. For example, if you are going to print it, there is no difference, as print $found is equivalent to print #found, because print takes a list of arguments.
If your intent is to interpolate the matches into a string:
print "Found matches: $found";
print "Found matches: ", #found;
$whole = join(' ', #lines)
But if you're reading the text from a file, it's easier to just read it all in one chunk, by (locally) undefining the record delimiter:
local $/ = undef;
$whole = <FILE>
Depends on what you are trying to do, but if you are wanting to package up an array into a scalar so that it can be retrieved later, then you might want Storable.
use Storable;
my #array = qw{foo bar baz};
my $stored_array = freeze \#array;
...
my #retrieved_array = #{ thaw($stored_array) };
Then again it could be that your needs may be served by just storing a reference to the array.
my #array = qw{foo bar baz};
my $stored_array = \#array;
...
my #retrieved_array = #$stored_array;