How to push data to a multidimensional array? - perl

I want to create 10 one dimensional arrays , and put these 10 one dimensional arrays to another one dimensional array , and store some data to some specific index of array .
but the output what I expect should be
expect output real output
0 1
1 1
0 1
3 1
0 1
5 1
0 1
7 1
0 1
0 1
here is my code
#Hits = ();
# Create 10 one dimension array
for($i=0;$i<=9;$i++)
{
#Space = ();
push(#Hits,\#Space);
}
# Store some data to some index
push(#{$Hits[1]},1);
push(#{$Hits[3]},3);
push(#{$Hits[5]},5);
push(#{$Hits[7]},7);
# print the first element of 10 arrays
for($i=0;$i<=9;$i++)
{
print $Hits[$i]->[0];
print "\n";
}
thanks

The Problem is that you aren't properly declaring your variables. For every script, you should
use strict; use warnings;
This disallows common error sources, warns about iffy stuff, and forces you to properly declare all your variables.
By default, all undeclared variables are considered global. Therefore, in
for($i=0;$i<=9;$i++)
{
#Space = ();
push(#Hits,\#Space);
}
the #Space refers to the same array in each iteration. Ergo, all ten entries in #Hits are a reference to the same array. Let's inspect what #Hits actually is. We can do so with Data::Dumper or the Data::Dump module (the latter usually produces prettier output):
use Data::Dump; # use Data::Dumper;
dd \#Hits; # print Dumper \#Hits;
We get with Data::Dumper (easier to understand):
$VAR1 = [
[
1,
3,
5,
7
],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0],
$VAR1->[0]
];
So I said the solution would be declaring your variables. Specifically, we want lexical variables. These variables are only visible inside the block where they are declared. This makes reasoning about code much easier. We can declare a lexical variable like so:
my $foo = 123;
When we have a loop like
my #Hits;
for my $i (0 .. 9) {
my #Space;
push #Hits, \#Space;
}
then each time the my is executed, we get a new #Space. Oh, and I have used a foreach-loop, that iterates over the range 0 .. 9 with the (lexical) $i variable. I find this easier to understand than the C-style loops you used.
Because every element in #Hits now is a different arrayref, we get the expected data structure. As Data::Dump output:
[[], [1], [], [3], [], [5], [], [7], [], []]
When we now execute your loop that prints out the first value of each sub-array, then you may be suprised by the empty lines in between the numebers. This is because e.g. the first arrayref does not have an entry at index 0, and therefore returns the special undef value. When used as a string, this is the empty string. If you followed my advice and did the use warnings, you also get a message that you are printing an uninitialized value.
We can solve this by testing for definedness, and providing a zero otherwise. Since perl5 v10, we can use the defined-or operator // for this (on earlier perls, the || logical or has to do).
for my $i (0 .. 9) {
my $value = $Hits[$i][0] // 0;
print "$value\n";
}
There are a few other bits we can improve:
We don't have to manually create the #Space arrays; Perl does this behind the scenes when you dereference an array entry like #{ $Hits[$i] }. This is called autovivification.
Not only can we iterate over ranges, but also over arrays. This is much better than hardcoding indices.
Since v10, you can use the say feature. The say function is exactly like print but appends a newline at the end.
Here is how I'd have written that code:
#!/usr/bin/perl
use strict; use warnings; use feature 'say';
my #Hits;
for my $i (1, 3, 5, 7) {
push #{ $Hits[$i] }, $i;
}
for my $arrayref (#Hits) {
say $arrayref->[0] // 0;
}
Output:
0
1
0
3
0
5
0
7
(Note that we never initialized values at positions 8 and 9, so they are not shown. We could amend this by iterating over the slice #Hits[0 .. 9].)

I change your code like following:
#! /usr/bin/perl -w
#Hits = ();
push(#{$Hits[1]},1);
push(#{$Hits[3]},3);
push(#{$Hits[5]},5);
push(#{$Hits[7]},7);
#print the content of
for($i=0;$i<=9;$i++)
{
if (defined ($Hits[$i])) {
print "$Hits[$i][0]\n";
} else {
print "0\n";
}
}
It's incorrect to give the ref of #space to #Hits and that makes the wrong result. It's not necessary to initial #Hits in perl.

perl -e "use Data::Dump; #sf=(); push #{$sf[0]},"0"; push #{$sf[1]},"1"; dd \#sf;"
[[0], [1]]
or
perl -e "use Data::Dump; #sf=(); push #sf,["0"]; push #sf,["1"]; dd \#sf;"
[[0], [1]]

Related

Why does perl insert an undef value into my hash?

Let me start off with a simple minimal example:
use strict;
use warnings;
use Data::Dumper;
my %hash;
$hash{count} = 4;
$hash{elems}[$_] = {} for (1..$hash{count});
print Dumper \%hash;
Here is the result (reformatted):
$VAR1 = {
'count' => 4,
'elems' => [undef, {}, {}, {}, {}]
};
I do not understand, why did the first element of $hash{elems} become an undef?
I know there are probably easier ways to do what I am doing, but I am creating these empty hashes so that I can later do my $e = $hash{elems}[$i] and continue to use $e to interact with the element, eg continue the horror of nested structures with $e->{subelems}[0] = 100.
Array indices start at 0 in Perl (and in most programming languages for that matter).
In the 1st iteration of $hash{elems}[$_] = {} for (1..$hash{count});, $_ is 1, and you thus put {} at index 1 of $hash{elems}.
Since you didn't put anything at index 0 of $hash{elems}, it contains undef.
To remedy this, you could use push instead of assigning to specific indices:
push #{$hash{elems}}, {} for 1 .. $hash{count};
push adds items at the end of its first argument. Initially, $hash{elems} is empty, so the end is the 1st index (0).
Some tips:
The parenthesis are not needed in for (1..$hash{count}): for 1 .. $hash{count} works just as well and looks a bit lighter.
You could initialize your hash when you declare it:
my %hash = (
count => 4,
elems => [ map { {} } 1 .. 4 ]
);
Initializing elems with an arrayref of hashrefs is often useless, thanks to autovivification. Simply doing $hash{elems}[0]{some_key} = 42 will create an arrayref in $hash{elems}, a hashref at index 0 in this array, containing the key some_key with value 42.
In some cases though, your initialization could make sense. For instance, if you want to pass $hash{elems} (but not $hash) to a function (same thing if you want to pass $hash{elems}[..] to a function without passing $hash{elems}).

Perl assigning #ARGV array to a variable

When I assign the Perl #ARGV array to a variable, if I don't use the quotes, it gives me the number of strings in the array, and not the strings in the array.
What is this called - I thought it was dereferencing, but it is not. Right now I am calling it one more thing I need to memorize in Perl.
#!/usr/bin/perl
use strict ;
use warnings;
my $str = "#ARGV" ;
#my $str = #ARGV ;
#my $str = 'geeks, for, geeks';
my #spl = split(', ' , $str);
foreach my $i (#spl) {
print "$i\n" ;
}
If you assign an array to a scalar in Perl, you get the number of elements in the array.
my #array = (1, 1, 2, 3, 5, 8, 13);
my $scalar = #array; # $scalar contains 7
This is known as "evaluating an array in scalar context".
If you expand an array in a double-quoted string in Perl, you get the elements of the array separated by spaces.
my #array = (1, 1, 2, 3, 5, 8, 13);
my $scalar = "#array"; $scalar contains '1 1 2 3 5 8 13'
This is known as "interpolating an array in a double-quoted string".
Actually, in that second example, the elements are separated by the current contents of the $" variable. And the default value of that variable is a space. But you can change it.
my #array = (1, 1, 2, 3, 5, 8, 13);
$" = '+';
my $scalar = "#array"; $scalar contains '1+1+2+3+5+8+13'
To store a reference to the array, you either take a reference to the array.
my $scalar = \#array;
Or create a new, anonymous array using the elements of of the original array.
my $scalar = [ #array ];
Because we don't know what you are actually trying to do, we can't recommend which of these is the best approach.
Perl works by context. The one that you see here is scalar versus array context. In scalar context, you want one thing, so Perl gives you the one thing the probably makes sense. Recognize the context and you can probably suss out what's going on.
When you have a scalar on the left side of an assignment, you have scalar context because you want to end up with one thing:
my $one_thing = ...
Put an array on the right side, and you have an array in scalar context. The design of Perl decided that the most common thing people probably want in that case is the number of elements:
my $one_thing = #array;
This works with some other builtins too. The localtime builtin returns a single string in scalar context (a timestamp):
my $uid = localtime; # Tue Mar 17 11:39:47 2020
But, in array context, you want possibly multiple things (where that could be two, or one, or zero, or ten thousand, or...). In that case, localtime returns a list of things:
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime();
You already know some of this though, probably. The + operator uses its operands as numbers, but the . operator uses them as strings:
my $sum = '123' + 14;
my $string = '123' . 14;
Perl's philosophy is that it is going to try to do what the verbs (operators, builtins, functions) are trying to do, not what the nouns (variable or value type) might imply. Many languages tell the verbs what to do based on the nouns, so fitting Perl into one of those mental modules usually doesn't work out. You don't have to memorize a lot; I've been doing this quite awhile and I still refer to the docs often.
We go through a lot of this philosophical explanation in Learning Perl.
The idiom you are looking for is one of
my $str = \#ARGV;
my $str = [ #ARGV ];
These both assign an array reference to the scalar variable $str. You can then get back the elements of #ARGV when you dereference $str. For example,
for my $i (#$str) {
print "$i\n";
}
(Some people prefer #{$str}, which does the same thing)
\ is the reference operator, which returns a reference to whatever is on its right hand side.
[...] creates a new array reference out of whatever is contained between the brackets.
"#array" is a stringify operation on an array, and equivalent to join($", #array)
And finally, a scalar assignment from an array, like
$n = #array
returns the number of elements in the array.
The total number of elements in the Array can sometimes be required. Since such situations are often encountered, we also have to learn how to get the total number.
#array = ("a".."z");
$re = $#array;
print ("$re\n");
We may need to add one to the number we get to reach the total number.
$ay = ("a", "b", "c");
$re = $#ay;
$re = $re +1;
print ("$re\n");
result: 3

How to print the values of array reference in PERL?

I have a defined days array with the square bracket in Perl. I want to access each element of the array.
A similar example from the code below(This is just a snippet of code):-
#days = [a,2,3];
foreach(#days){print "$_\n";}
print "\n\n #days";
And output is
ARRAY(0x2032950)
ARRAY(0x2032950)
I need to access the array elementS but I cannot change the #days declaration.
The following code is not working as well:-
#days = [a,2,3];
use feature qw<say>;
foreach(#days){print "$_\n";}
print "\n\n #days\n";
print "#$days\n";
say $_ for $days->#*;
Attn: OP - array declaration is not correct.
If you can not change array declaration (it is not clear what is the cause) then print them with following code
use strict;
use warnings;
use feature 'say';
my #days = ['a',2,3];
say for #{$days[0]};
say "Number of elements: " . scalar #{$days[0]};
Proper code should be
use strict;
use warnings;
use feature 'say';
my #days = ('a',2,3);
say for #days;
say "Number of elements: " . scalar #days;
Following piece of code demonstrates how array created, using this information is easy to figure out how to access stored values of array elements
use strict;
use warnings;
use feature 'say';
use Data::Dumper;
my #days = ['a',2,3];
say Dumper(\#days);
Output
$VAR1 = [
[
'a',
2,
3
]
];
I think you accidentally have an extra layer in your data.
When you use the square braces, you are actually using the anonymous array constructor. That returns an array reference, which is a scalar (single item). You probably didn't mean to assign that to an array since you get an array of one element where that one element is the reference. This effectively makes a double-level hash:
my #days = [ 'a', 1, 2 ]; # probably wrong
Since the array reference is a scalar, you likely mean to assign it to a scalar with the $ (single item) sigil. you don't use # because the reference points to an array. The sigil is more about the container than the data:
my $days = [ 'a', 1, 2 ];
When you have the array reference, there are various ways to get its elements. Since it's a simple scalar (not a single element access to an array or hash), you can prefix it with # (the multiple element sigil) to treat it as an array:
my #elements = #$days;
# OR
foreach my $element ( #$days ) {
say "Element: $element";
}
You can even interpolate that just like a named array:
say "Elements are #$days";
Here's a way to print the array reference:
#! /usr/bin/env perl
use warnings;
use strict;
use feature qw<say>;
my $arr_ref = [1,2,3];
say for $arr_ref->#*;

Handling with two warnings of #ARGS

Small debug question I can't solve for some reason. Consider the following code:
use warnings;
my $flag = 0;
foreach my $i (0..scalar(#ARGV)) {
$data{$OPTION} .= $ARGV[$i]." " if($flag);
$flag = 1 if($ARGV[$i] =~ /$OPTION/);
undef $ARGV[$i] if($flag);
}
I get the following two warnings:
Use of uninitialized value within #ARGV in concatenation (.) or string at line 4
Use of uninitialized value in pattern match (m//) at line 5
I get the reason is that I undefine some value of #ARGV and then it tries to check it.
The way I do it like this is because I would like to 'cut' some of the data of #ARGV before using GetOpt module (which uses this array).
How to solve it?
Let's expand on those comments a bit.
Imagine #ARGV contains four elements. They will have the indexes 0, 1, 2 and 3 (as arrays in Perl are zero-based).
And your loop looks like this:
foreach my $i (0..scalar(#ARGV)) {
You want to visit each element in #ARGV, so you use the range operator (..) to generate a list of all those indexes. But scalar #ARGV returns the number of elements in #ARGV and that's 4. So your range is 0 .. 4. And there's no value at $ARGV[4] - so you get an "undefined value" warning (as you're trying to read past the end of an array).
A better way to do this is to use $#ARGV instead of scalar #ARGV. For every array variable in Perl (say #foo) you also get a variable (called $#foo) which contains the last index number in the array. In our case, that's 3 and your range (0 .. $#ARGV) now contains the integers 0 .. 3 and you no longer try to read past the end of the array and you don't get the "undefined value" warnings.
There's one other improvement I would suggest. Inside your loop, you only ever use $i to access an element from #ARGV. It's only used in expressions like $ARGV[$i]. In this case, it's probably better to skip the middle man and to iterate across the elements in the array, not the indexes.
I mean you can write your code like this:
foreach my $arg (#ARGV) {
$data{$OPTION} .= $arg . " " if($flag);
$flag = 1 if($arg =~ /$OPTION/);
undef $arg if($flag);
}
I think that's a little easier to follow.

multidimensional array: argument isn't numeric in array element

OS: AIX
Shell: KSH
Following the accepted answer on this question I have created an multimensional array. Only, I get an error while trying to print the content of the array.
Error:
Argument "content of $pvid" isn't numeric in array element at...
The script:
#!/usr/bin/perl
use warnings;
use strict;
use Term::ANSIColor;
my #arrpvid = ();
print colored( sprintf("%-10s %9s %8s %8s %8s", 'PVID', 'AIX', 'VIO', 'VTD', 'VHOST'), 'green' ), "\n";
foreach my $pvid (`lspv | awk '{print \$2'}`) {
foreach my $hdaix (`lspv | awk '{print \$1'}`) {
chomp $pvid;
chomp $hdaix;
push #{ $arrpvid[$pvid] }, $hdaix;
}
}
print $arrpvid[0][0];
Some explanation:
Basically I want to print 5 variables of 5 different arrays next to each other. The code is written only for 2 arrays.
The content of $pvid:
00088da343b00d9b
00088da38100f93c
The content of $hdaix:
hdisk0
hdisk1
Quick Fix
Looks like you want to use a hash rather than an array, making your inner push
push #{ $arrpvid{$pvid} }, $hdaix;
Note the change from square brackets to curly braces immediately surrounding $pvid. This tells the compiler that you want %arrpvid and not #arrpvid, so be sure to tweak your my declaration as well.
At the end to print the contents of %arrpvid, use
foreach my $pvid (sort { hex $a <=> hex $b } keys %arrpvid) {
local $" = "]["; # handy trick due to mjd
print "$pvid: [#{$arrpvid{$pvid}}]\n";
}
The Data::Dumper module is quick and easy output tool.
use Data::Dumper;
$Data::Dumper::Indent = $Data::Dumper::Terse = 1;
print Dumper \%arrpvid;
More Details
You might be tempted to obtain the numeric value corresponding to each hexadecimal string in $pvid with hex as in
push #{ $arrpvid[hex $pvid] }, ...
but given the large example values in your question, #arrpvid would become enormous. Use a hash to create a sparse array instead.
Be sure that all the values of $pvid have the same padding. Otherwise, like values may not hash together appropriately. If you need to normalize, use code along the lines of
$pvid = sprintf "%016x", hex $pvid;
The problem lies in:
push #{ $arrpvid[$pvid] }, $hdaix;
The $pvid should be a numeric value like 0 or 5 and not i.e. 00088da343b00d9b