Perl return array from multidimensional array - arrays

I've got this multidimensional array:
#valid_values = (["val00", "val01", "val02"], ["val10", "val11", "val12"]);
Via grep I want to check, if a certain value exists within the first array of the multidimensional array.
I've tried something like this:
if (grep $_ eq $check_value, #valid_values[0]) {print "ok\n"}
This doesn't work though. I've also tried using $valid_values[0], but I think this will only return a reference to the array, so I used # to dereference it.
Any suggestions on how I can return the whole first array of the 2d-array, so I can use it for the grep function?

The first element of #valid_values is accessed as $valid_values[0].
The value in the first element is an array reference. To dereference an array reference, you use #{ ... }.
So to get the array referenced by the array reference in the first element of #valid_values you want #{ $valid_values[0] }.
For more details about how to deal with data structures like this, see perllol, perldsc and perlreftut.

Related

2D Array Printing as Reference

I have the code similar to below:
my #array1 = (); #2d array to be used
my $string1 = "blank1";
my $string2 = "blank2";
my $string3 = "blank3";
my #temp = ($string1, $string2, $string3);
push (#array1, \#temp);
The reason I am assigning the strings and then putting them into an array is because they are in a loop and the values get updated in the loop (#array1 is not declared in the loop).
When I run my program, it only gives me a reference to an array rather than an actual 2D array. How can I get it to print out the content as a 2D array and not as a reference or flattened out to a 1D array?
I would like an output like [[blank1, blank2, blank3],....] so i can access it like $array1[i][j]
An array can only have scalars for elements. Thus this includes references, to arrays for example, what enables us to build complex data structures. See perldsc, Tom's Perl Data Structure Cookbook.
Elements of those ("second-level") arrays are accessed by dereferencing, so $array1[0]->[1] is the second element of the array whose reference is the first element of the top-level array (#array1). Or, for convenience, a simpler syntax is allowed as well: $array1[0][1].
If we want a list of all elements of a second-level array then dereference it with #, like:
my #l2 = #{ $array1[0] }; # or, using
my #l2 = $array1[0]->#*; # postfix dereferencing
Or, to get just a few elements of the array, but in one scoop -- a slice
my #l2_slice = #{$array1[0]}[1..2]; # or
my #l2_slice = $array1[0]->#[1..2]; # postfix reference slice
what returns the list with the second and third elements of the same second-level array.
The second lines are of a newer syntax called postfix dereferencing, stable as of v5.24. It avails us with the same logic for getting elements as when we drill for a single one, by working left-to-right all the way. So ->#* to get a list of all elements for an arrayref,->%* for a hashref (etc). See for instance a perl.com article and an Effective Perler article.
There is a thing to warn about when it comes to multidimensional structures built with references. There are two distinct ways to create them: by using references to existing, named, variables
my #a1 = 5 .. 7;
my %h1 = ( a => 1, b => 2 );
my #tla1 = (\#a1, \%h1);
or by using anonymous ones, where arrayrefs are constructed by [] and hashrefs by {}
my #tla2 = ( [ 5..7 ], { a => 1, b => 2 } );
The difference is important to keep in mind. In the first case, the references to variables that the array carries can be used to change those variables -- if we change elements of #tla1 then we really change the referred variables
$tla1[0][1] = 100; # now #a1 == 5, 100, 7
Also, changing variables with references in #tla1 is seen via the top-level array as well.
With anonymous arrays and hashes in #tla this isn't the case as elements (references) of #tla access independent data, which cannot be accessed (and changed) in any other way.
Both of these ways to build complex data structures have their uses.

Perl: Negative range indexing of array reference

I have an array reference that I would like to slice the last two elements of the array. I found that using -2..-1 would work. I was using the following syntax:
subroutine($var->[-2..-1]);
This gave me the following error:
Use of uninitialized value $. in range (or flip)
Argument "" isn't numeric in array element
I changed the line to this and that worked:
subroutine(#$var[-2..-1]);
I don't understand why the second way works though and the first doesn't. I thought using the array operator was the same as dereferencing with #. Is the context ambiguous with the arrow operator?
-> is the dereference operator. $aref->[$i] is to an $aref like $arr[$i] is to #arr. To get a slice from an array, you need to change the sigil: #arr[$i, $j]. It's similar for dereference, but instead of changing the sigil, you first dereference the reference, then slice it:
#{ $aref }[$i, $j]
which can be shortened to #$aref[$i, $j].
So the -> operator can only be used for single values for array and hash references. You need #{} for slices.

push ELEMENTS of array to an array using Perl

using Perl I am trying to push the elements of an array to another array, and not the whole array. But I'm not getting my goal.
I tried this:
push #tmp_entities_all, #tmp_entities;
But I got the whole small array as an element in the bigger array.
Then I tried it with a loop:
for (#tmp_entities) {push #tmp_entities_all, $_;}
But the same results, the whole #tmp_entities appears as an element, and that what I dont want.
I need one dimension in the array and not array of arrays!! Should I cast something before pushing? or what is the problem?
Thanx a lot.
Out of your comments, its obvious, that #tmp_entities contains only one element, that is an array reference to the elements that you expected to be elements of #tmp_entities.
So you perhaps declared your array with an array refence instead of using a set of elements.
The line
push #tmp_entities_all, #tmp_entities;
definitly works for a normal array.
In your case, your could try ...
push #tmp_entities_all, $tmp_entities[0];
or you simply try to initialize your array with its value like
my #tmp_entities = ( 1, 2, 3 ); # initialize array with 3 elements of type int
instead of
my #tmp_entities = [ 1, 2, 3 ]; # initialize array with 1 element that is an array reference with 3 elements of type int
I know, that this is the case, because this is why your for-loop sample works with #$_ ;D (it is equivalent to push #tmp_entities_all, $tmp_entities[0]; in this situation).

How to create two dimensional array with 2 arrays

How to create multiple dimentional array with 2 arrays?
#param=("param1","param2","param3");
#value=("value1_1, value1_2, value1_3", "value2_1, value2_2, value2_3","value3_1, value3_2, value3_3");
Output:
#out=(["param1"]["value1_1", "value1_2", "value1_3"], ["param2"]["value2_1", "value2_2", "value2_3"], ["param3"]["value3_1", "value3_2", "value3_3"])
I've tried this way:
$j=0;
foreach $i(#param1){
push #{$out[$i]}, split(", ", $value[$j]);
$j++;}
It's not exactly clear to me what data structure you want to create.
However, I assume you are trying to create a hash of arrays (a hash table is also known as dictionary or associative array), not an array.
The difference in Perl is that an array always uses integers as keys, whereas a hash always uses strings.
The resulting data structure would then look like
%out = (
'param1' => ['value1_1', 'value1_2', 'value1_3'],
'param2' => ['value2_1', 'value2_2', 'value2_3'],
'param3' => ['value3_1', 'value3_2', 'value3_3'],
);
We can create this data structure like so:
my %out;
for my $i (0 .. $#param) {
$out{$param[$i]} = [split /,\s*/, $value[$i]];
}
Note that $#foo is the highest index in the #foo array. Therefore, 0 .. $#foo would be the range of all indices in #foo. Also note that entries in hashes are accessed with a curly brace subscript $hash{$key}, unlike arrays which use square brackets $array[$index].
You can access multiple elements in a hash or array at the same time by using a slice – #foo{'a', 'b', 'c'} is equivalent to ($foo{a}, $foo{b}, $foo{c}). We can also transform a list of elements by using the map {BLOCK} LIST function. Together, this allows for the following solution:
my %out;
#out{#param} = map { [split /,\s*/, $_] } #value;
Inside the map block, the $_ variable is set to each item in the input list in turn.
To learn more about complex data structures, read (in this order):
perldoc perreftut
perldoc perllol
perldoc perldsc
You can also read the documentation for the map function and for the foreach loop.

Fun with array of arrays

I'm a total Perl newb, but still cannot believe I cannot figure this out with all the info I've read through online, but, I've burned too much time and am suffering from block at this point. Hoping to learn something based on my real life example...
Ok, I think I have an array of arrays, created like this:
my #array1 = ();
my #array2 = ();
my $ctr1 = 0;
my $col;
[sql query]
while(($col)=$sth->fetchrow_array() ) {
$array1[$ctr1]=$col;
$ctr1++;
}
print STDERR "#array1";
##results in 10 rows, a mac address in each
##00:00:00:00:00:00 00:11:11:11:11:11 22:22:22:22:22:22 33:33:33:33:33:33 ...
Now I do another query. While looping through results, I am looking for those 10 mac addresses. When I find one, I add a row to array2 with the mac and the sequential number accumulated to the point, like this:
[sql query]
while(($col)=$sth->fetchrow_array() ) {
$ctr2++;
if( my ($matched) = grep $_ eq $col, #array1 ) {
push( #array2, ($col,$ctr2) );
}
}
print STDERR "#array2";
##results in 10 rows, a mac address and an integer in each
##00:00:00:00:00:00 2 00:11:11:11:11:11 24 22:22:22:22:22:22 69 33:33:33:33:33:33 82 ...
Now the easy part. I want to loop through array2, grabbing the mac address to use as part of a sql query. Therein lies the problem. I am so ignorant as to exactly what I am doing that even though I had it almost working, I can't get back to that point. Ignorance is definitely not bliss.
When I loop through array2, I am getting a host of errors, based on the different forms of the statement. The one I think is right is listed below along with the error message...
my $ctr3 = 0;
foreach $ctr3 (#array2) {
my $chkmac = $array2[$ctr3][0]; <--- gacks here with the error below - line 607
[SQL query]
[Thu May 30 14:05:09 2013] [error] Can't use string ("00:66:55:77:99:88") as an ARRAY ref while "strict refs" in use at /path/to/test.cgi line 607.\n
I believe the issue is that my array of arrays is not an array of arrays. If it were, it would work as coded, or so I think from the reading... That said, I cannot fathom what I am dealing with otherwise. This will be a head slapper I'm all but sure, but I am stumped.... Little help, please?
TIA
O
For an array of arrays you want to use an array reference, e.g.
push #array2, [$col, $ctr2];
When accessing an element within an array refernce, you'll want to use the -> operator. Also, when looping through an array, it's not necessary to index back into that same array. So the last part would look more like:
foreach $ctr3 (#array2) {
my $chkmac = $ctr3->[0];
....
When you do the foreach there, $ctrl3 won't have the index in it, it'll have the value. So you should just need to do $ctrl3->[0]. Note the -> which dereferences the array reference (#array2 is actually an array of array references).
EDIT: As AKHolland pointed out, #array2 actually isn't an array of array references, although that's what it should be. You also need to change:
push( #array2, ($col, $ctr2) );
To
push( #array2, [$col, $ctr2] );
This makes an array reference, rather than a list. A list in this context just collapses down into regular arguments to push, meaning you're pushing two separate strings into #array2.
You are correct that your array of arrays is not an array of arrays, since in Perl there is no such thing. So what do you have instead? There's two ways to see.
First, when you print #array2, you come up with a string composed of alternating MACs and counts, separated by spaces. Since the spaces sort-of-signify the division between array elements, we might surmise that what we've got is a single array of heterogeneous elements, such that element 0 is a MAC, element 1 is a count, element 2 is another MAC, and so on.
The other perspective is to look at how #array2 is constructed:
push( #array2, ($col,$ctr2) );
From the documentation for push, we find that push ARRAY LIST works by appending the elements of LIST to the end of ARRAY. This has the effect of flattening the list into the array such that its original identity as a list is lost. You can add all the parentheses you want, when Perl expects a list it flattens all of them away.
So how do you achieve the effect you want? The List-of-Lists documentation has a detailed treatment, but the short answer is that you make a list of array references. Array references are scalars and are therefore legal elements in an array. But they retain their identify as array references!
The anonymous array reference constructor is the square bracket []. In order to push an array reference containing the elements $col and $ctr2 onto the end of #array2, you simply do this:
push( #array2, [$col, $ctr2] );
The code you wrote for accessing a particular element of the array-reference-in-an-array now works. But since I've already written a bunch of paragraphs on the subject, let me finish by explaining what was wrong originally and how changing the push statements suddenly makes it work.
The expression $array2[$ctr3][0] is sometimes written as $array2[$ctr3]->[0] to clarify what it's actually doing. What it does is to take the value of $array2[$ctr3] and treat it as an array reference, taking its 0 element. If we take $ctr3 to be 0 (as it would be at the top of the loop) the value of $array2[$ctr3] is the first element, 00:00:00:00:00:00. When you then subsequently ask Perl to treat 00:00:00:00:00:00 as an array reference, Perl dies because it doesn't know how to treat a string as an array reference.
When instead the value of $array2[$ctr3] is an array reference because that is what you pushed onto #array2 when constructing it, Perl is able to do as you ask, dereferencing the array reference and looking at element 0 of the resulting array, whose value happens to be 00:00:00:00:00:00.

Resources