How can I find out if elements of an array match any elements of another array? - perl

if I have a hash
my %foo = ( foo => 1, bar => 1 );
I want to check if any key of %foo is in a comparison array (and obviously keys %foo is just an array ). I keep thinking some weird syntax that does't exist like.
my #cmp0 = qw( foo baz );
my #cmp1 = qw( baz blargh );
if keys %foo in #cmp0 # returns true because key foo is in the array
if keys %foo in #cmp1 # returns false because no key in foo is an element of cmp1
What is the simplest way to do this?

List::MoreUtils has a function called any that uses a syntax similar to grep, but stops its internal loop the first time the criteria are met. The advantage to this behavior is that far fewer iterations will be required (assuming random distribution of intersections).
An additional advantage of any is code clarity: It is named for what it does. Perl Best Practices discourages using grep in Boolean context because the assumed use for grep is to return a list of elements that match. It works in Boolean context, but the intent of the code is less clear to a reader than any, which is designed specifically for Boolean usage.
It is true that any adds a dependency on List::MoreUtils. However, List::MoreUtils is one of those modules that is so ubiquitous, it is highly likely to already be installed.
Here's an example:
use List::MoreUtils qw( any );
my %foo = ( foo => 1, bar => 1 );
my #cmp0 = qw( foo baz );
my #cmp1 = qw( baz blargh );
print "\#cmp0 and %foo have an intersection.\n"
if any { exists $foo{$_} } #cmp0;
print "\#cmp1 and %foo have an intersection.\n"
if any { exists $foo{$_} } #cmp1;
Another option is the ~~ Smart Match Operator, which became available in Perl 5.10.0 and newer. It could be used like this:
my %foo = ( foo => 1, bar => 1 );
my #cmp0 = qw( foo baz );
my #cmp1 = qw( baz blargh );
print "\#cmp0 and %foo have an intersection.\n" if #cmp0 ~~ %foo;
print "\#cmp1 and %foo have an intersection.\n" if #cmp1 ~~ %foo;
With smartmatch, you eliminate the List::MoreUtils dependency in favor of a minimum Perl version dependency. It's up to you to decide whether the code is as clear as any.

The tidiest way to write this is to use grep together with the exists operator.
This code
my %foo = ( foo => 1, bar => 1 );
my #cmp0 = qw( foo baz );
my #cmp1 = qw( baz blargh );
print "YES 0\n" if grep { exists $foo{$_} } #cmp0;
print "YES 1\n" if grep { exists $foo{$_} } #cmp1;
gives this output
YES 0

grep is a good idea, and probably the cleanest. You can however also use the logical OR assignment operator ||=:
my $found;
$found ||= exists $foo{$_} for #cmp1;

These are simple set operations.
use strictures;
use Set::Scalar qw();
⋮
my $foo = Set::Scalar->new(keys %foo);
$foo->intersection(Set::Scalar->new(#cmp0))->size; # 1
$foo->intersection(Set::Scalar->new(#cmp1))->size; # 0

There are - as usual - more ways to solve this. You could do it like this:
#!/usr/bin/perl
use strict ;
use warnings ;
my %hash = ( foo => 1 , bar => 1 ) ;
my %cmp = ( cmp0 => [ qw(foo baz) ] ,
cmp1 => [ qw(baz blargh) ] ) ;
my #hash_keys = keys %hash ;
foreach my $compare ( keys %cmp ) {
my %tmp ;
# Generate a temporary hash from comparison keys via hash slice
#tmp{#{$cmp{$compare}}} = undef ;
INNER:
foreach my $hash_key ( #hash_keys ) {
if( exists $tmp{$hash_key} ) {
printf "Key '%s' is part of '%s'.\n" , $hash_key , $compare ;
last INNER ;
}
}
}
This gives:
Key 'foo' is part of 'cmp0'.

Related

How to check if an array has an element that's not Integer?

For example, I have an array = [1,2,3,4,5,a,#,4]
how can I check if an array has an element that's not integer?
If an array has an element that's not integer than I am planning to fail the script which is die function.
Type::Tiny provides a quick method (all) to check if all elements in a list match a specific type consrtaint.
use strict; use warnings; use feature qw( say );
use Types::Common qw( Int PositiveInt );
my #array = ( 1, 2, 3, "foo" );
if ( not Int->all( #array ) ) {
say "They're not all integers";
}
if ( PositiveInt->all( #array ) ) {
say "They're all positive integers";
}
# Another way to think about it:
my $NonInteger = ~Int;
if ( $NonInteger->any( #array ) ) {
say "There's at least one non-integer";
}
If quick&dirty is good enough:
my #a = (1,2,3,-4,0,5,4," -32 ");
die if grep { !defined or !/^\s*-?\d+\s*$/ } #a;
This however doesn't handle for example valid integers (in Perl) such as "1_200_499" or "0e0" well.

Perl hash, array and references

I have this 3 lines of code in a sub and I'm trying to write them together on one line only.. but I'm quite lost
my %p = #_;
my $arr = $p{name};
my #a = #$arr;
what's the correct way of doing this?
thank you!
my %p = #_;
#_ is assumed to contain key-value pairs which are then used to construct the hash %p.
my $arr = $p{name};
The argument list is assumed to have contained something along the lines of name, [1, 2, 3,] so that $p{name} is an reference to an array.
my #a = #$arr;
Dereference that array reference to get the array #.
Here is an invocation that might work with this prelude in a sub:
func(this => 'that', name => [1, 2, 3]);
If you want to reduce the whole prelude to a single statement, you can use:
my #a = #{ { #_ }->{name} };
as in:
#!/usr/bin/env perl
use strict;
use warnings;
use YAML::XS;
func(this => 'that', name => [1, 2, 3]);
sub func {
my #a = #{ { #_ }->{name} };
print Dump \#a;
}
Output:
---
- 1
- 2
- 3
If the array pointed to by name is large, and if you do not need a shallow copy, however, it may be better to just stick with references:
my $aref = { #_ }->{ name };
OK so what you're doing is:
Assign a list of elements passed to the sub, to a hash.
extract a value from that hash (that appears to be an array reference)
dereference that into a standalone array.
Now, I'm going to have to make some guesses as to what you're putting in:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub test {
my %p = #_;
my $arr = $p{name};
my #a = #$arr;
print Dumper \#a;
}
my %input = ( fish => [ "value", "another value" ],
name => [ "more", "less" ], );
test ( %input );
So with that in mind:
sub test {
print join "\n", #{{#_}->{name}},"\n";
}
But actually, I'd suggest what you probably want to do is pass in the hashref in the first place:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub test {
my ( $input_hashref ) = #_;
print Dumper \#{$input_hashref -> {name}};
}
my %input = ( fish => [ "value", "another value" ],
name => [ "more", "less" ], );
test ( \%input );
Also:
Don't use single letter variable names. It's bad style.
that goes double for a and b because $a and $b are for sorting. (And using #a is confusing as a result).

Merge two or more hash slices, i.e. only selected keys from the hashes

I am writing a piece of code that receives arguments %args and has a configuration %conf. I need to pass certain values from both of these hashes to another Perl module while others are relevant only for my own code.
How to merge two (or more) hashes slices or merge hashes while keeping only selected keys in an elegant way?
Important points:
The list of desired keys might be used as list or saved in an array.
Values from %args shall override those from %conf.
Keys not contained in %args nor %conf shall not be contained in %result.
Example input:
my %conf = (
path => '/usr/local/bin/',
size => 42,
other => 'value', # isn't used
);
my %args = (
path => '~/bin/', # overrides $conf{path}
foo => 'bar',
);
my #keys = qw<path size foo bar>; # 'bar' isn't contained in either hash!
Expected result:
my %result = (
path => '~/bin/', # from $args{patħ}
size => 42,
foo => 'bar',
);
For a simple merge this is enough:
my %result = ( %conf, %args );
A simple hash slice is also simple:
my %slice = map { $_ => $hash{$_} } qw<foo bar baz>;
Reducing to the existing keys already needs a temporary variable:
my %keys = map { $_ => 1 } qw<foo bar baz>;
grep { $keys{$_} } %hash;
But putting it alltogether becomes quite complex:
my #keys = qw<foo bar baz>;
my %keys = map { $_ => 1 } #keys;
my %result = (
map( { $_ => $conf{$_} } grep { $keys{$_} } keys %conf ),
map( { $_ => $args{$_} } grep { $keys{$_} } keys %args ),
);
Is there some nicer way to code this?
Update
This is a response to your revised question. My best effort to write something clean just deletes any elements of the combined hash that aren't in #keys, using an intermediate %wanted hash to describe which hash keys appear in the array
use strict;
use warnings 'all';
# Set up test data
my %conf = (
path => '/usr/local/bin/',
size => 42,
other => 'value', # isn't used
);
my %args = (
path => '~/bin/', # overrides $conf{path}
foo => 'bar',
);
my #keys = qw/ path size foo bar /; # 'bar' isn't contained in either hash!
# Combine and select
my %wanted = map { $_ => 1 } #keys;
my %result = (%conf, %args);
delete #result{ grep { not $wanted{$_} } keys %result };
# Display the result
use Data::Dump;
dd \%result;
output
{ foo => "bar", path => "~/bin/", size => 42 }
Original
This seems to fit the bill. I've copied the test data from your post
The two hashes are combined into %join, with %args overriding %conf by putting it second in the list assignment
Then the array %result is built by using slices to pull the required elements from %join
I've used Data::Dump only to show the results of the preceding code
Note that if there is a string in #keys that doesn't appear in either hash, then it will be included in %results with a value of undef
“The list of desired keys might be used as list or saved in an array.”
That's a very vague requirement. For this solution to work you need an array of keys, so simply store your list as an array
use strict;
use warnings 'all';
# Set up test data
my %conf = (
path => '/usr/local/bin/',
size => 42,
other => 'value', # isn't used
);
my %args = (
path => '~/bin/', # overrides $conf{path}
foo => 'bar',
);
my #keys = qw/ path size foo /;
# Combine and select
my %join = (%conf, %args);
my %result;
#result{#keys} = #join{#keys};
# Display the result
use Data::Dump;
dd \%result;
output
{ foo => "bar", path => "~/bin/", size => 42 }

In perl, can I dynamically create variables within a subroutine?

Background
In the code I'm writing, I'm passing data into methods using a hash-ref (see note [1]).
This, unfortunately, leads to a lot of repetitive code:
sub thing {
my ($self, $params) = #_;
my ($foo, $bar, $baz, $biff,);
if ( exists $params->{foo} && $params->{foo} ) {
$foo = $params->{foo};
}
# repeat for `bar`, `baz`, `biff`
## rest of function ##
}
(and duplicate in every function with parameters)
What I want to do
What would be far easier is to define a list of parameters, and then
iterate of that list, creating both the variables and setting them to a value if needed.
So to test this, I tried:
my $params = { x => 1, y => 2};
my #params = qw(x y z a b c);
gno strict 'refs';
rep( ${$_}, #params );
use strict 'refs';
foreach my $p (#params) {
if ( exists $params->{$p} && $params->{$p} ) {
${$p} = $params->{$p};
}
}
print "x:$x, y:$y, z:$z, a:$a, b:$b, c:$c\n"
which gives me the following error:
Global symbol "$x" requires explicit package name at ./test.pl line 20.
Global symbol "$y" requires explicit package name at ./test.pl line 20.
Global symbol "$z" requires explicit package name at ./test.pl line 20.
Global symbol "$c" requires explicit package name at ./test.pl line 20.
Can I do this dynamic variable creation thing? (and if so, how?)
[1] By using a hash to pass data in, I gain in many ways:
There is a clear indication of What each item of data is
The ORDER of the pieces of data is no longer important
I can miss one or more pieces of data, and I don't need to add in random undef values
I'm passing less data: 1 scalar (a reference) rather than multiple scalars
(I accept the danger of functions being able to change the parent's data, rather that mucking around with a copy of it...)
Yes, you can do this in Perl. But it's a terrible idea for all of the reasons explained by Mark Dominus in these three articles.
It's a far better idea to store these values in a hash.
#!/usr/bin/perl
use strict;
use warnings;
my $params = { x => 1, y => 2};
my #params = qw(x y z a b c);
my %var;
foreach my $p (#params) {
# You need to take care exactly what you want in this
# logical statement. The options are:
# 1/ $p exists in the hash
# exists $params->{$p}
# 2/ $p exists in the hash and has a defined value
# defined $params->{$p}
# 3/ $p exists in the hash and has a true value
# $params->{$p}
# I think the first option is most likely. The last one has
# good chance of introducing subtle bugs.
if ( exists $params->{$p} ) {
$var{$p} = $params->{$p};
}
}
print join ', ', map { "$_: " . ($var{$_} // 'undef') } #params;
print "\n";
It's a really bad idea to use symbolic references like this... hashes pretty well completely eliminate the need for this.
use warnings;
use strict;
my $params = { x => 1, y => 2, foo => 3, };
thing($params);
sub thing {
my $params = shift;
my $foo;
if (defined $params->{foo}){
$foo = $params->{foo};
}
print $foo;
}
You can also pass in a hash itself directly (whether it be pre-created, or passed inline to the sub. If pre-created, the sub will operate on a copy).
thing(foo => 1, x => 2);
sub thing {
my %params = #_;
print $params{foo} if defined $params{foo};
}
With thanks to Dave Cross & others - the following test works:
#!/usr/bin/perl
use strict;
use warnings;
use English qw( -no_match_vars ) ;
use Carp;
use Data::Dumper;
my $params = { x => 1, y => 2, z => 0};
my #params = qw(x y z a b c);
my %var;
foreach my $p (#params) {
if ( exists $params->{$p} ) {
$var{$p} = $params->{$p};
} else {
$var{$p} = undef;
}
}
print Dumper \%var;
This gives me %var with all desired parameters (as listed in #params, with the ones that are not passed in (ie, not in the $params hashref) created with an undef value.
Thus I can confidently test for value and truth, without worrying about existence.
Thank you all.
I did this using soft references:
#!perl
no strict "refs";
my %vars = ( x => 1, y => 2 );
for my $k ( keys %vars ) {
$$k = $vars{$k};
}
print $x, $y;
But there's a reason why the recommended settings (use strict; use warnings;) prevent this kind of pattern. It is easy to shoot yourself in the foot with it.
perl -Mstrict -MData::Dumper -wE'
{package Data::Dumper;our($Indent,$Sortkeys,$Terse,$Useqq)=(1)x4}
my #aok = qw( x y z a b c );
my %dft = ( a => -1 );
say "- - - -";
my $arg = { x => 1, y => 2, foo => 42 };
$arg = { %dft, %$arg };
say "arg: ", Dumper($arg);
my %var;
#var{ #aok } = #$arg{ #aok };
say "var: ", Dumper(\%var);
my %aok = map { $_ => 1 } #aok;
my #huh = grep !$aok{$_}, sort keys %$arg;
#huh and say "huh: ", Dumper(\#huh);
'
- - - -
arg: {
"a" => -1,
"foo" => 42,
"x" => 1,
"y" => 2
}
var: {
"a" => -1,
"b" => undef,
"c" => undef,
"x" => 1,
"y" => 2,
"z" => undef
}
huh: [
"foo"
]

How can I check if a Perl scalar holds a reference to a certain subroutine?

In other words, how can I check for coderef "equality"?
The smartmatch operator doesn't work for obvious reasons (would treat it as CODE->(ANY)), but I've included it in the example to show what I'm after:
use strict;
use warnings;
use feature 'say';
sub pick_at_random {
my %table = #_;
return ( values %table )[ rand( keys %table ) ];
}
my %lookup = ( A => \&foo,
B => \&bar,
C => \&baz );
my $selected = pick_at_random( %lookup );
say $selected ~~ \&foo ? "Got 'foo'" :
$selected ~~ \&bar ? "Got 'bar'" :
$selected ~~ \&baz ? "Got 'baz'" :
"Got nadda" ;
You can use normal (numeric) equality (==), as is the case with all references:
Perl> $selected == \&foo
Perl> $selected == \&bar
Perl> $selected == \&baz
1
Live in action here
That breaks when the reference is blessed with something that overloads == or 0+ (which is unlikely for coderefs). In that case, you'd compare Scalar::Util::refaddr($selected).
From man perlref:
Using a reference as a number produces an integer representing its storage location in memory. The only useful thing to be done with this is to compare two
references numerically to see whether they refer to the same location.
if ($ref1 == $ref2) { # cheap numeric compare of references
print "refs 1 and 2 refer to the same thing\n";
}