I have had this question for a while.
Assuming I have a simple array,
my #arr;
foreach my $i (0..$#arr){
print "\$arr[$i] = '$arr[$i]'\n";
}
$#arr simply returns the last index of the array
however, what happens if I want to use it for something more complex like?:
foreach my $i (0..$##{$someHash{$}[$b]{$c}});
sure, I can write it as
foreach my $i (0..(scalar(#{$someHash{$}[$b]{$c}})-1));
But that just makes the code even more complex.
So my question is, can the $# be used for anything else other than a simple array( references, a list returned from a function call, etc...) and if so, how?
Thanks
Yes, you want:
for my $i (0..$#{$some_hash{$a}[$b]{$c}}) { ... }
If possible, though, I generally prefer
for my $value (#{ $some_hash{$a}[$b]{$c} }) { ... }
an index value is rarely as useful as the value stored in the array. Typically, if I need both, I add a counter instead:
my $i = 0;
for my $value (#{ $some_hash{$a}[$b]{$c} }) { ... } continue { $i++ }
and for clarity, I will also pre-assign the complex variable in the loop:
my $i = 0;
my $widget_list = $some_hash{$a}[$b]{$c};
for my $value (#{ $widget_list }) { ... } continue { $i++ }
Just because you can do some things in one line in Perl doesn't necessarily mean you should.
Yes, it can be done:
sub foo {
return { 'bar' => { 'array' => [ 'qux', [qw/ has three elements /] ] } };
}
print $#{ foo->{'bar'}->{'array'}->[1] }; # 2
When writing such code, my thought process is: get to the array with the #{} syntax and then replace # with $#.
If it's
$#array
for an array, it's
$#{ $array_ref }
for a reference. Of course, it can be any expression that returns an array reference, so you want the following:
$#{ $someHash{$}[$b]{$c} }
See Mini-Tutorial: Dereferencing Syntax
Related
my $memType = [];
my $portOp = [];
my $fo = "aster.out.DRAMA.READ.gz";
if($fo =~/aster.out\.(.*)\.(.*)\.gz/){
push (#{$memType},$1);
push (#{$portOp},$2);
}
print Dumper #{$memType};
foreach my $mem (keys %{$portCapability->{#{$memType}}}){
//How to use the array ref memType inside a hash//
print "entered here\n";
//cannot post the rest of the code for obvious reasons//
}
I am not able to enter the foreach loop . Can anyone help me fix it?
Sorry this is not the complete code . Please help me.
%{$portCapability->{#{$memType}}}
This doesn't do what you may think it means.
You treat $portCapability->{#{$memType}} as a hash reference.
The #{$memType} is evaluated in scalar context, thus giving the size of the array.
I aren't quite sure what you want, but would
%{ $portCapability->{ $memType->[0] } }
work?
If, however, you want to slice the elements in $portCapability, you would need somethink like
#{ $portCapability }{ #$memType }
This evaluates to a list of hashrefs. You can then loop over the hashrefs, and loop over the keys in an inner loop:
for my $hash (#{ $portCapability }{ #$memType }) {
for my $key (keys %$hash) {
...;
}
}
If you want a flat list of all keys of the inner hashes, but don't need the hashes themselves, you could shorten above code to
for my $key (map {keys %$_} #{ $portCapability }{ #$memType }) {
...;
}
I think what you want is this:
my $foo = {
asdf => {
a => 1, b => 2,
},
foo => {
c => 3, d => 4
},
bar => {
e => 5, f => 6
}
};
my #keys = qw( asdf foo );
foreach my $k ( map { keys %{ $foo->{$_} } } #keys ) {
say $k;
}
But you do not know which of these $k belongs to which key of $foo now.
There's no direct way to get the keys of multiple things at the same time. It doesn't matter if these things are hashrefs that are stored within the same hashref under different keys, or if they are seperate variables. What you have to do is build that list yourself, by looking at each of the things in turn. That's simply done with above map statement.
First, look at all the keys in $foo. Then for each of these, return the keys inside that element.
my $memType = [];
my $portOp = [];
my $fo = “aster.out.DRAMA.READ.gz”;
if ($fo =~ /aster.out\.(\w+)\.(\w+)\.gz/ ) { #This regular expression is safer
push (#$memType, $1);
push (#$portOp, $2);
}
print Dumper “#$memType”; #should print “DRAMA”
#Now if you have earlier in your program the hash %portCapability, your code can be:
foreach $mem (#$memType) {
print $portCapability{$mem};
}
#or if you have the hash $portCapability = {…}, your code can be:
foreach $mem (#$memType) {
print $portCapability->{$mem};
}
#Hope it helps
Goal: Remove a particular Value from an array
I have written a script and it works fine but I am not happy the way I have written it. So I am curious to know is there a better way to write it. please consider below use case:
I have a nested hash/hash/array...like below. I need to remove any array values which has local in their name:
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
my $hash = { esx1 =>
{ cluster => "clu1",
fd => "fd1",
ds => [
'ds1',
'ds2',
'localds',
],
},
esx2 =>
{ cluster => "clu2",
fd => "fd2",
ds => [
'ds3',
'ds4',
'dslocal',
],
},
};
foreach my $a ( keys %$hash )
{
foreach ( 0..$#{ $hash->{$a}->{ds} } )
{
delete $hash->{$a}->{ds}->[$_] if $hash->{$a}->{ds}->[$_] =~ /local/i;
#{ $hash->{$a}->{ds} } = grep defined, #{ $hash->{$a}->{ds} };
}
}
print Dumper ($hash);
so the script deletes the "localds" and "dslocal" and keeps everything else intact.
Question:
Is there a cleaner way to write the foreach ( 0..$#{$hash->{$a}->{ds} } ) loop
If I do not write the grep line above, the resultant array has the value containing local deleted but is replaced by undef. Why is this happening.
Thanks.
delete is for elements in hashes. It happens to work on arrays by a quirk of implementation, but it shouldn't be relied on.
For arrays, you want to use splice.
splice #{ $ref->{to}->{array} }, $index, 1, ();
This replaces the 1-element sublist starting at $index with (), the empty list.
Why first iterate through array and delete elements and then look for "not-deleted" nodes (side note - this grep should be outside loop)? You can look for good nodes from very start! Replace entire loop with:
foreach my $a ( keys %$hash )
{
#{ $hash->{$a}->{ds} } = grep { !/local/i } #{ $hash->{$a}->{ds} };
}
for my $h (values %$hash){
$h->{ds} = [ grep { $_ !~ /local/i } #{$h->{ds}} ];
}
Not much neater but:
foreach my $a ( keys %$hash )
{
my $temp;
foreach ( #{ $hash->{$a}->{ds} } )
{
push(#$temp, $_) unless $_ =~ /local/i;
}
$hash->{$a}->{ds} = $temp;
}
Delete doesn't alter the array structure, it just alters the array content. Because of this in your method you need to grep for defined entries to create a new array of the structure you desire, then overwrite the old array.
edit:
This is better explained on perldoc page for delete
delete() may also be used on arrays and array slices, but its behavior is less straightforward. Although exists() will return false for deleted entries, deleting array elements never changes indices of existing values; use shift() or splice() for that. However, if all deleted elements fall at the end of an array, the array's size shrinks to the position of the highest element that still tests true for exists(), or to 0 if none do.
edit:
As pointed out by mob splice will do what you want:
foreach my $a ( keys %$hash )
{
for(0..$#{ $hash->{$a}->{ds} } )
{
splice( #{ $hash->{$a}->{ds} }, $_, 1 ) if $hash->{$a}->{ds}->[ $_ ] =~ /local/i;
}
}
You could use List::MoreUtils qw/first_idx/ to get the index of /local/ like so
first_idx { $_ =~ /local/i } #{$hash->{$a}->{ds}};
Then do what you want with that.
In Perl, I have a hash of hashes created with a loop similar to the following
my %HoH
for my $i (1..10) {
$HoH{$a}{$b} = $i;
}
$a and $b are variables that do have some value when the HoH gets filled in. After creating the HoH, how can I check if a particular pair ($c, $d) exists in the HoH? The following does not work
if (defined $HoH{$c}{$d}) {...}
because if $c does not exist in HoH already, it will be created as a key without a value.
Writing
if (defined $HoH{$c}{$d}) {...}
will "work" insomuch as it will tell you whether or not $HoH{$c}{$d} has a defined value. The problem is that if $HoH{$c} doesn't already exist it will be created (with an appropriate value) so that $HoH{$c}{$d} can be tested. This process is called "autovivification." It's convenient when setting values, e.g.
my %hoh;
$hoh{a}{b} = 1; # Don't need to set '$hoh{a} = {}' first
but inconvenient when retrieving possibly non-existent values. I wish that Perl was smart enough to only perform autovivification for expressions used as lvalues and short-circuit to return undef for rvalues but, alas, it's not that magical. The autovivification pragma (available on CPAN) adds the functionality to do this.
To avoid autovivification you need to test the intermediate values first:
if (exists $HoH{$c} && defined $HoH{$c}{$d}) {
...
}
use Data::Dumper;
my %HoH;
$HoH{A}{B} = 1;
if(exists $HoH{C} && exists $HoH{C}{D}) {
print "exists\n";
}
print Dumper(\%HoH);
if(exists $HoH{C}{D}) {
print "exists\n";
}
print Dumper(\%HoH);
Output:
$VAR1 = {
'A' => {
'B' => 1
}
};
$VAR1 = {
'A' => {
'B' => 1
},
'C' => {}
};
Autovivification is causing the keys to be created. "exists" in my second example shows this so the first example checks both keys individually.
Several ways:
if ( $HoH{$c} && defined $HoH{$c}{$d} ) {...}
or
if ( defined ${ $HoH{$c} || {} }{$d} ) {...}
or
no autovivification;
if (defined $HoH{$c}{$d}) {...}
or
use Data::Diver;
if ( defined Data::Diver::Dive( \%HoH, $c, $d ) ) {...}
You have to use the exists function
exists EXPR
Given an expression that specifies an
element of a hash, returns true if the
specified element in the hash has ever
been initialized, even if the
corresponding value is undefined.
Note that the EXPR can be arbitrarily
complicated as long as the final
operation is a hash or array key
lookup or subroutine name:
if (exists $ref->{A}->{B}->{$key}) { }
if (exists $hash{A}{B}{$key}) { }
My take:
use List::Util qw<first>;
use Params::Util qw<_HASH>;
sub exists_deep (\[%$]#) {
my $ref = shift;
return unless my $h = _HASH( $ref ) // _HASH( $$ref )
and defined( my $last_key = pop )
;
# Note that this *must* be a hash ref, for anything else to make sense.
return if first { !( $h = _HASH( $h->{ $_ } )) } #_;
return exists $h->{ $last_key };
}
You could also do this recursively. You could also create a descent structure allowing intermediate and even terminal arrayref with just a little additional coding.
I'm looking for presence of an element in a list.
In Python there is an in keyword and I would do something like:
if element in list:
doTask
Is there something equivalent in Perl without having to manually iterate through the entire list?
UPDATE:
The smartmatch family of features are now experimental
Smart match, added in v5.10.0 and significantly revised in v5.10.1, has been a regular point of complaint. Although there are a number of ways in which it is useful, it has also proven problematic and confusing for both users and implementors of Perl. There have been a number of proposals on how to best address the problem. It is clear that smartmatch is almost certainly either going to change or go away in the future. Relying on its current behavior is not recommended.
Warnings will now be issued when the parser sees ~~, given, or when.
If you can get away with requiring Perl v5.10, then you can use any of the following examples.
The smart match ~~ operator.
if( $element ~~ #list ){ ... }
if( $element ~~ [ 1, 2, 3 ] ){ ... }
You could also use the given/when construct. Which uses the smart match functionality internally.
given( $element ){
when( #list ){ ... }
}
You can also use a for loop as a "topicalizer" ( meaning it sets $_ ).
for( #elements ){
when( #list ){ ... }
}
One thing that will come out in Perl 5.12 is the ability to use the post-fix version of when. Which makes it even more like if and unless.
given( $element ){
... when #list;
}
If you have to be able to run on older versions of Perl, there still are several options.
You might think you can get away with using List::Util::first, but there are some edge conditions that make it problematic.
In this example it is fairly obvious that we want to successfully match against 0. Unfortunately this code will print failure every time.
use List::Util qw'first';
my $element = 0;
if( first { $element eq $_ } 0..9 ){
print "success\n";
} else {
print "failure\n";
}
You could check the return value of first for defined-ness, but that will fail if we actually want a match against undef to succeed.
You can safely use grep however.
if( grep { $element eq $_ } 0..9 ){ ... }
This is safe because grep gets called in a scalar context. Arrays return the number of elements when called in scalar context. So this will continue to work even if we try to match against undef.
You could use an enclosing for loop. Just make sure you call last, to exit out of the loop on a successful match. Otherwise you might end up running your code more than once.
for( #array ){
if( $element eq $_ ){
...
last;
}
}
You could put the for loop inside the condition of the if statement ...
if(
do{
my $match = 0;
for( #list ){
if( $element eq $_ ){
$match = 1;
last;
}
}
$match; # the return value of the do block
}
){
...
}
... but it might be more clear to put the for loop before the if statement.
my $match = 0;
for( #list ){
if( $_ eq $element ){
$match = 1;
last;
}
}
if( $match ){ ... }
If you're only matching against strings, you could also use a hash. This can speed up your program if #list is large and, you are going to match against %hash several times. Especially if #array doesn't change, because then you only have to load up %hash once.
my %hash = map { $_, 1 } #array;
if( $hash{ $element } ){ ... }
You could also make your own subroutine. This is one of the cases where it is useful to use prototypes.
sub in(&#){
local $_;
my $code = shift;
for( #_ ){ # sets $_
if( $code->() ){
return 1;
}
}
return 0;
}
if( in { $element eq $_ } #list ){ ... }
if( $element ~~ #list ){
do_task
}
~~ is the "smart match operator", and does more than just list membership detection.
grep is helpful here
if (grep { $_ eq $element } #list) {
....
}
If you plan to do this many times, you can trade-off space for lookup time:
#!/usr/bin/perl
use strict; use warnings;
my #array = qw( one ten twenty one );
my %lookup = map { $_ => undef } #array;
for my $element ( qw( one two three ) ) {
if ( exists $lookup{ $element }) {
print "$element\n";
}
}
assuming that the number of times the element appears in #array is not important and the contents of #array are simple scalars.
List::Util::first
$foo = first { ($_ && $_ eq "value" } #list; # first defined value in #list
Or for hand-rolling types:
my $is_in_list = 0;
foreach my $elem (#list) {
if ($elem && $elem eq $value_to_find) {
$is_in_list = 1;
last;
}
}
if ($is_in_list) {
...
A slightly different version MIGHT be somewhat faster on very long lists:
my $is_in_list = 0;
for (my $i = 0; i < scalar(#list); ++$i) {
if ($list[i] && $list[i] eq $value_to_find) {
$is_in_list = 1;
last;
}
}
if ($is_in_list) {
...
TIMTOWTDI
sub is (&#) {
my $test = shift;
$test->() and return 1 for #_;
0
}
sub in (#) {#_}
if( is {$_ eq "a"} in qw(d c b a) ) {
print "Welcome in perl!\n";
}
List::MoreUtils
On perl >= 5.10 the smart match operator is surely the easiest way, as many others have already said.
On older versions of perl, I would instead suggest List::MoreUtils::any.
List::MoreUtils is not a core module (some say it should be) but it's very popular and it's included in major perl distributions.
It has the following advantages:
it returns true/false (as Python's in does) and not the value of the element, as List::Util::first does (which makes it hard to test, as noted above);
unlike grep, it stops at the first element which passes the test (perl's smart match operator short circuits as well);
it works with any perl version (well, >= 5.00503 at least).
Here is an example which works with any searched (scalar) value, including undef:
use List::MoreUtils qw(any);
my $value = 'test'; # or any other scalar
my #array = (1, 2, undef, 'test', 5, 6);
no warnings 'uninitialized';
if ( any { $_ eq $value } #array ) {
print "$value present\n"
}
P.S.
(In production code it's better to narrow the scope of no warnings 'uninitialized').
Probably Perl6::Junction is the clearest way to do. No XS dependencies, no mess and no new perl version required.
use Perl6::Junction qw/ any /;
if (any(#grant) eq 'su') {
...
}
This blog post discusses the best answers to this question.
As a short summary, if you can install CPAN modules then the best solutions are:
if any(#ingredients) eq 'flour';
or
if #ingredients->contains('flour');
However, a more usual idiom is:
if #any { $_ eq 'flour' } #ingredients
which i find less clear.
But please don't use the first() function! It doesn't express the intent of your code at all. Don't use the "Smart match" operator: it is broken. And don't use grep() nor the solution with a hash: they iterate through the whole list. While any() will stop as soon as it finds your value.
Check out the blog post for more details.
PS: i'm answering for people who will have the same question in the future.
You can accomplish a similar enough syntax in Perl if you do some Autoload hacking.
Create a small package to handle the autoload:
package Autoloader;
use strict;
use warnings;
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my ($method) = (split(/::/, $AUTOLOAD))[-1];
die "Object does not contain method '$method'" if not ref $self->{$method} eq 'CODE';
goto &{$self->{$method}};
}
1;
Then your other package or main script will contain a subroutine that returns the blessed object which gets handled by Autoload when its method attempts to be called.
sub element {
my $elem = shift;
my $sub = {
in => sub {
return if not $_[0];
# you could also implement this as any of the other suggested grep/first/any solutions already posted.
my %hash; #hash{#_} = ();
return (exists $hash{$elem}) ? 1 : ();
}
};
bless($sub, 'Autoloader');
}
This leaves you with usage looking like:
doTask if element('something')->in(#array);
If you reorganize the closure and its arguments, you can switch the syntax around the other way to make it look like this, which is a bit closer to the autobox style:
doTask if search(#array)->contains('something');
function to do that:
sub search {
my #arr = #_;
my $sub = {
contains => sub {
my $elem = shift or return;
my %hash; #hash{#arr} = ();
return (exists $hash{$elem}) ? 1 : ();
}
};
bless($sub, 'Autoloader');
}
I have a perl variable $results that gets returned from a service. The value is supposed to be an array, and $results should be an array reference. However, when the array has only one item in it, $results will be set to that value, and not a referenced array that contains that one item.
I want to do a foreach loop on the expected array. Without checking ref($results) eq 'ARRAY', is there any way to have something equivalent to the following:
foreach my $result (#$results) {
# Process $result
}
That particular code sample will work for the reference, but will complain for the simple scalar.
EDIT: I should clarify that there is no way for me to change what is returned from the service. The problem is that the value will be a scalar when there is only one value and it will be an array reference when there is more than one value.
im not sure there's any other way than:
$result = [ $result ] if ref($result) ne 'ARRAY';
foreach .....
Another solution would be to wrap the call to the server and have it always return an array to simplify the rest of your life:
sub call_to_service
{
my $returnValue = service::call();
if (ref($returnValue) eq "ARRAY")
{
return($returnValue);
}
else
{
return( [$returnValue] );
}
}
Then you can always know that you will get back a reference to an array, even if it was only one item.
foreach my $item (#{call_to_service()})
{
...
}
Well if you can't do...
for my $result ( ref $results eq 'ARRAY' ? #$results : $results ) {
# Process result
}
or this...
for my $result ( ! ref $results ? $results : #$results ) {
# Process result
}
then you might have to try something hairy scary like this!....
for my $result ( eval { #$results }, eval $results ) {
# Process result
}
and to avoid that dangerous string eval it becomes really ugly fugly!!....
for my $result ( eval { $results->[0] } || $results, eval { #$results[1 .. $#{ $results }] } ) {
# Process result
}
PS. My preference would be to abstract it away in sub ala call_to_service() example given by reatmon.
I would re-factor the code inside the loop and then do
if( ref $results eq 'ARRAY' ){
my_sub($result) for my $result (#$results);
}else{
my_sub($results);
}
Of course I would only do that if the code in the loop was non-trivial.
I've just tested this with:
#!/usr/bin/perl -w
use strict;
sub testit {
my #ret = ();
if (shift){
push #ret,1;
push #ret,2;
push #ret,3;
}else{
push #ret,"oneonly";
}
return \#ret;
}
foreach my $r (#{testit(1)}){
print $r." test1\n";
}
foreach my $r (#{testit()}){
print $r." test2\n";
}
And it seems to work ok, so I'm thinking it has something to do with the result getting returned from the service?
If you have no control over the returning service this might be hard one to crack
You can do it like this:
my #some_array
push (#some_array, results);
foreach my $elt(#some_array){
#do something
}