How do I determine the number of elements in an array reference? - perl

Here is the situation I am facing...
$perl_scalar = decode_json( encode ('utf8',$line));
decode_json returns a reference. I am sure this is an array. How do I find the size of $perl_scalar?? As per Perl documentation, arrays are referenced using #name. Is there a workaround?
This reference consist of an array of hashes. I would like to get the number of hashes.
If I do length($perl_scalar), I get some number which does not match the number of elements in array.

That would be:
scalar(#{$perl_scalar});
You can get more information from perlreftut.
You can copy your referenced array to a normal one like this:
my #array = #{$perl_scalar};
But before that you should check whether the $perl_scalar is really referencing an array, with ref:
if (ref($perl_scalar) eq "ARRAY") {
my #array = #{$perl_scalar};
# ...
}
The length method cannot be used to calculate length of arrays. It's for getting the length of the strings.

You can also use the last index of the array to calculate the number of elements in the array.
my $length = $#{$perl_scalar} + 1;

$num_of_hashes = #{$perl_scalar};
Since you're assigning to a scalar, the dereferenced array is evaluated in a scalar context to the number of elements.
If you need to force scalar context then do as KARASZI says and use the scalar function.

You can see the entire structure with Data::Dumper:
use Data::Dumper;
print Dumper $perl_scalar;
Data::Dumper is a standard module that is installed with Perl. For a complete list of all the standard pragmatics and modules, see perldoc perlmodlib.

Related

Comparison to an array of a value [duplicate]

This question already has answers here:
How can I verify that a value is present in an array (list) in Perl?
(8 answers)
Closed 9 years ago.
I'm still feeling my way though perl and so there's probably a simple way of doing this but I can find it. I want to compare a single value say A or E to an array that may or may not contain that value, eg A B C D and then perform an action if they match. How should I set this up?
Thanks.
You filter each element of the array to see if it is the element you are looking for and then use the resulting array as a boolean value (not empty = true, empty = false):
#filtered_array = grep { $_ eq 'A' } #array;
if (#filtered_array) {
print "found it!\n";
}
If you store the list in an array then the only way is to examine each element individually in a loop, using grep, or for or any from List::MoreUtils. (grep is the worst of these, as it searches the entire array, even if a match has been found early on.) This is fine if the array is small, but you will hit performance probelms if the array has a significant size and you have to check it frequently.
You can speed things up by representing the same list in a hash, when a check for membership is just a single key lookup.
Alternatively, if the list is enormous, then it is best kept in a database, using SQLite.
Are you stuck on arrays?
Whenever in Perl you're talk about quickly looking up data, you should think in terms of hashes. A hash is a collection of data like an array, but it is keyed, and looking up the key is a very fast operation in Perl.
There's nothing that says the keys to your hash can't be your data, and it is very common in Perl to index an array with a hash in order to quickly search for values.
This turns your array #array into a hash called %arrays_hash.
use strict;
use warnings;
use feature qw(say);
use autodie;
my #array = qw(Alpha Beta Delta Gamma Ohm);
my %array_index;
for my $entry ( #array ) {
$array_index{$entry} = 1; # Doesn't matter. As long as it isn't blank or zero
}
Now, looking up whether or not your data is in your array is very quick. Just simply see if it's a key in your %array_index:
my $item = "Delta"; # Is this in my initial array?
if ( $array_index{$item} ) {
say "Yes! Item '$item' is in my array.";
}
else {
say "No. Item '$item' isn't in my array. David sad.";
}
This is so common, that you'll see a lot of programs that use the map command to index the array. Instead of that for loop, I could have done this:
my %array_index = ( map { $_ => 1 } #array );
or
my %array_index;
map { $array_index{$_} = 1 } #array;
You'll see both. The first one is a one liner. The map command takes each entry in the array, and puts it in $_. Then, it returns the results into an array. Thus, the map will return an array with your data in the even positions (0, 2, 4 8...) and a 1 in the odd positions (1, 3, 5...).
The second one is more literal and easier to understand (or about as easy to understand in a map command). Again, each item in your #array is being assigned to $_, and that is being used as the key in my %array_index hash.
Whether or not you want to use hashes depend upon the length of your array, and how many items of input you'll be searching for. If you're simply searching whether a single item is in your array, I'd probably use List::Utils or List::MoreUtils, or use a for loop to search each value of my array. If I am doing this for multiple values, I am better off with a hash.

perl 101, getting length of array from using $ on the array variable

I was expecting this to give the length of the array. Since I thought $mo implied scalar context.
But instead, I get the error :
Global symbol "$mo" requires explicit package name at ./a.pl line 7.
#! /usr/bin/perl
use strict;
use warnings;
my #mo = (3,4,5);
print( $mo);
UPDATE::
I thought mo is the variable and the sigil $ on $mo is using scalar context. My question is more on the sigil then actually getting the length.
In order to get the number of elements in #mo use scalar #mo.
my $num_elements = scalar #mo;
You can omit the scalar when the context dictates that it must be scalar, such as in a comparison:
if ($count < #mo) { print "$count is less than the number of elements" }
You can also use $#mo, which is the index of the last element (generally one less than the number of elements).
my $last_index = $#mo;
This is useful when you are iterating through an array and need the array index:
for (0..$#mo)
{
print "Index $_ is $mo[$_]\n";
}
The $mo form is used when obtaining an element of the array:
my $second_element = $mo[1];
$mo just by itself is a totally separate variable (though you probably shouldn't create such a variable, as it would be confusing).
You are trying to print a scalar variable $mo which does not exist. You need to use the array name in scalar context as:
my #mo = (3,4,5);
print scalar #mo;
Another way is to use $#mo which would return the largest index in the array which in your case is 2.
You may get length of an array as
my $mo = #mo;
print $mo;
my $mo = scalar (#mo);
print $mo;
my $mo = $#mo + 1; print $mo;

How can I create a multi-dimensional array in perl?

I was creating a multi-dimensional array this way:
#!/usr/bin/perl
use warnings;
use strict;
my #a1 = (1, 2);
my #a2 = (#a1, 3);
But it turns out that I still got a one-dimensional array...
What's the right way in Perl?
You get a one-dimensional array because the array #a1 is expanded inside the parentheses. So, assuming:
my #a1 = (1, 2);
my #a2 = (#a1, 3);
Then your second statement is equivalent to my #a2 = (1,2,3);.
When creating a multi-dimensional array, you have a few choices:
Direct assignment of each value
Dereferencing an existing array
Inserting a reference
The first option is basically $array[0][0] = 1; and is not very exciting.
The second is doing this: my #a2 = (\#a1, 3);. Note that this makes a reference to the namespace for the array #a1, so if you later change #a1, the values inside #a2 will also change. It is not always a recommended option.
A variation of the second option is doing this: my #a2 = ([1,2], 3);. The brackets will create an anonymous array, which has no namespace, only a memory address, and will only exist inside #a2.
The third option, a bit more obscure, is doing this: my $a1 = [1,2]; my #a2 = ($a1, 3);. It will do exactly the same thing as 2, only the array reference is already in a scalar variable, called $a1.
Note the difference between () and [] when assigning to arrays. Brackets [] create an anonymous array, which returns an array reference as a scalar value (for example, that can be held by $a1, or $a2[0]).
Parentheses, on the other hand, do nothing at all really, except change the precedence of operators.
Consider this piece of code:
my #a2 = 1, 2, 3;
print "#a2";
This will print 1. If you use warnings, you will also get a warning such as: Useless use of a constant in void context. Basically, this happens:
my #a2 = 1;
2, 3;
Because commas (,) have a lower precedence than equal sign =. (See "Operator Precedence and Associativity" in perldoc perlop.)
Parentheses simply negate the default precedence of = and ,, and group 1,2,3 together in a list, which is then passed to #a2.
So, in short, brackets, [], have some magic in them: They create anonymous arrays. Parentheses, (), just change precedence, much like in math.
There is much to read in the documentation. Someone here once showed me a very good link for dereferencing, but I don't recall what it was. In perldoc perlreftut you will find a basic tutorial on references. And in perldoc perldsc you will find documentation on data structures (thanks Oesor for reminding me).
I would propose to work through perlreftut, perldsc and perllol, preferably in the same day and preferably using Data::Dumper to print data structures.
The tutorials complement each other and I think they would take better effect together. Visualizing data structures helped me a lot to believe they actually work (seriously) and to see my mistakes.
Arrays contain scalars, so you need to add a reference.
my #a1 = (1,2);
my #a2 = (\#a1, ,3);
You'll want to read http://perldoc.perl.org/perldsc.html.
The most important thing to understand
about all data structures in
Perl--including multidimensional
arrays--is that even though they might
appear otherwise, Perl #ARRAY s and
%HASH es are all internally
one-dimensional. They can hold only
scalar values (meaning a string,
number, or a reference). They cannot
directly contain other arrays or
hashes, but instead contain references
to other arrays or hashes.
Now, because the top level contains only references, if you try to print out your array in with a simple print() function, you'll get something that doesn't look very nice, like this:
#AoA = ( [2, 3], [4, 5, 7], [0] );
print $AoA[1][2];
7
print #AoA;
ARRAY(0x83c38)ARRAY(0x8b194)ARRAY(0x8b1d0)
That's because Perl doesn't (ever) implicitly dereference your variables. If you want to get at the thing a reference is referring to, then you have to do this yourself using either prefix typing indicators, like ${$blah} , #{$blah} , #{$blah[$i]} , or else postfix pointer arrows, like $a->[3] , $h->{fred} , or even $ob->method()->[3]
Source: perldoc
Now coming to your question. Here's your code:
my #a1 = (1,2);
my #a2 = (#a1,3);
Notice that the arrays contain scalar values. So you have to use reference and you can add a reference by using the \ keyword before an array's name which is to be referenced.
Like this:
my #a2 = (\#a1, ,3);
Inner arrays should be scalar references in the outer one:
my #a2 = (\#a1,3); # first element is a reference to a1
print ${$a2[0]}[1]; # print second element of inner array
This is a simple example of a 2D array as ref:
my $AoA = undef;
for(my $i=0; $i<3; $i++) {
for(my $j=0; $j<3; $j++) {
$AoA->[$i]->[$j] = rand(); # Assign some value
}
}

How do I get a slice from an array reference?

Let us say that we have following array:
my #arr=('Jan','Feb','Mar','Apr');
my #arr2=#arr[0..2];
How can we do the same thing if we have array reference like below:
my $arr_ref=['Jan','Feb','Mar','Apr'];
my $arr_ref2; # How can we do something similar to #arr[0..2]; using $arr_ref ?
To get a slice starting with an array reference, replace the array name with a block containing the array reference. I've used whitespace to spread out the parts, but it's still the same thing:
my #slice = # array [1,3,2];
my #slice = # { $aref } [1,3,2];
If the reference inside the block is a simple scalar (so, not an array or hash element or a lot of code), you can leave off the braces:
my #slice = #$aref[1,3,2];
Then, if you want a reference from that, you can use the anonymous array constructor:
my $slice_ref = [ #$aref[1,3,2] ];
With the new post-dereference feature (experimental) in v5.20,
use v5.20;
use feature qw(postderef);
no warnings qw(experimental::postderef);
my #slice = $aref->#[1,3,2];
Just slice the reference (the syntax is similar to dereferencing it, see the comments), and then turn the resulting list back into a ref:
my $arr_ref2=[#$arr_ref[0..2]];
my $arr_ref2 = [ #$arr_ref[0..2] ];

how to grep perl Hash Keys in to an array?

Iam a perl newbie and need help in understanding the below piece of code.
I have a perl Hash defined like this
1 my %myFavourite = ("Apple"=>"Apple");
2 my #fruits = ("Apple", "Orange", "Grape");
3 #myFavourite{#fruits}; # This returns Apple. But how?
It would be great if perl gurus could explain what's going on in Line-3 of the above code.
myFavourite is declared has a hash,but used as an array? And the statement simply takes the key of the hash ,greps it in to the array and returns the hash values corresponding the key searched. Is this the way we grep Hash Keys in to the Array?
It doesn't return Apple. It evaluates to a hash slice consisting of all of the values in the hash corresponding to the keys in #fruits. Notice if you turn on warnings that you get 2 warnings about uninitialized values. This is because myFavourite does not contain values for the keys Orange and Grape. Look up 'hash slice' in perldata.
Essentially, #myFavourite{#fruits} is shorthand for ($myFavourite{Apple}, $myFavourite{Orange}, $myFavourite{Grape}), which in this case is ($myFavourite{Apple},undef,undef). If you print it, the only output you see is Apple.
myFavourite is declared has a hash,but used as an array?
Yes, and it returns a list. It's a hash slice. See: http://perldoc.perl.org/perldata.html
Think of it as an expansion of array #fruits into multiple hash key lookups.
The #hash{#keys} syntax is just a handy way of extracting portions of the hash.
Specifically:
#myFavourite{#fruits}
is equivalent to:
($myFavourite{'Apple'},$myFavourite{'Orange'},$myFavourite{'Grape'})
which returns a three item list if called in list context or a concatenation of all three elements in scalar context (e.g. print)
my #slice_values = #myFavourite{#fruits}
# #slice_values now contains ('Apple',undef,undef)
# which is functionally equivalent to:
my #slice_values = map { $myFavourite{$_} } #fruits;
If you want to only extract hash values with keys, do:
my #favourite_fruits = #myFavourite{ grep { exists $myFavourite{$_} } #fruits };
# #favourite_fruits now contains ('Apple')
If you:
use warnings;
you'll see the interpreters warnings about the two uninitialized values being autovivified as undef.