How to check whether hash has a value for the key in puppet - hash

I have a hash defined as below:
Hash[String, String] $hashtest = { "abc" => "test1", "xyz" => "test2" },
I have String variable, I need to search for the given key in the hash and if a value is found, I need to assign that value to the variable "result" otherwise I need to assign a default value "test". How can I do this is in puppet? Or only way to do this is using if else condition?
It should be similar like this, but the below code is not working. Kindly correct me what I'm doing wrong.
String $variable = $hashtest[$key] ? { true => $hashtest[$key], false => "test" },
It would be really helpful if someone helps me with this thanks in advance.

I am assuming in your pseudocode you are intending to assign a value with a return from a selector, and not also providing a pseudocode for a ternary-like expression in Puppet. With that in mind, we can achieve this with something similar to Python:
String $variable = $key in $hashtest ? {
true => $hashtest[$key]
false => "test"
}
Note that prior to Puppet 4 you would need the has_key? function (analogous to has_key Hash method in Ruby) from stdlib:
String $variable = has_key($hashtest, $key) ? {
true => $hashtest[$key]
false => 'test'
}
In stdlib there is also a function roughly equivalent to a "null coalescing" operator in other languages (null being roughly equivalent to undef type in Puppet and nil in Ruby) that would provide a cleaner expression:
String $variable = pick($hashtest[$key], 'test')
Similar to the coalescing patterns in other languages, pick will return the first argument that is not undef or empty.

As well as matts answer you can also use the following
$variable = $hashtest[$key].lest || { 'test' }
$variable = ($key in $hashtest).bool2str($hashtest[$key], 'test')
$variable = $hashtest.has_key($key).bool2str($hashtest[$key], 'test')

All of these options are missing the most simple and powerful option that's been available from the core library since puppet 6; the get function.
The get function allows you specify a dot separated path of nested keys to look up as the first argument, and a default value for the second. By default, it will return undef if a value cannot be found, making it ideal for use in conditional expressions since undef is the only value in puppet that automatically converts to false. You can even pass a lambda to it to handle missing values.
In your case, the answer is as simple as $variable = $hashtest.get($key, 'test') or $variable = get($hashtest, $key, 'test'), though I personally find the first option easier to read.

Related

Perl syntax (replacing 'defined(#array')

Let me start by saying I haven't programmed in Perl in a LONG time.
Currently trying to get some older code to work that relies on defined with an array.
Code (abridged):
# defined outside the file-reading block
my %refPRE;
# line from a file
#c = split();
if (defined #{$refPRE{$c[0]}})
{
# do stuff
}
Now this won't run like this because of the following error:
Can't use 'defined(#array)' (Maybe you should just omit the defined()?)
Fine, but if I removed the defined then I get the following error:
Can't use an undefined value as an ARRAY reference.
I can see what it's trying to do (if $c[0] is in the $refPRE then do this, else do something else) but I'm not familiar enough with Perl to figure out what the right way to get this to work is. Hoping this is trivial for someone.
Apparently posting here is all the catalyst I needed...
Switching if (defined #{$refPRE{$c[0]}}) to if ($refPRE{$c[0]}) was sufficient to work! Hopefully this helps someone else who searching for this (specific) problem...
Can't use an undefined value as an ARRAY reference.
This is saying that $refPRE{ $c[0] } is returning undef, and you cannot dereference undef as an array.
#{ undef } # will error
You don't need to deref this at all. If it returns undef, it's false. If it returns anything else, that will (likely) be true.
if ( $refPRE{$c[0]} )
{
my $foo = #{ $refPRE{$c[0]} };
# do stuff
}
Looking at your second error $refPRE{$c[0]} can be undefined so #{ ... } is failing. You can fix this using the undef or opperator // like this.
if (#{ $refPRE{$c[0]} // [] }) { ... }
This checks if $refPRE{$c[0]} is defined and if not returns an empty anonymous array. An empty array is false in an if statement.

Perl error, cant use defined(#array). How can i fix this?

I am getting this error -> "Can't use 'defined(#array)' (Maybe you should just omit the defined()?)"
On this line of code:
if ( defined( #{ $linkbot{"$nroboton"} } ) ) {
How can i fix this?
defined tests whether a scalar value is undef, therefore it's nonsensical on an array. You can test whether the scalar is defined before using it as an arrayref, or if you're trying to test if the array is empty, just removed the defined() as the error message says.
# if this hash value is defined
if (defined $linkbot{$nroboton}) {
# if this referenced array has elements
if (#{ $linkbot{$nroboton} }) {
Use define on the variable $nroboton itself, and/or if (#{$linkbot{$nroboton}}) for the anonymous array whose reference is the value for that key, as explained.
Once you need to check on any of that it stands to reason that you may well also need to test whether there is a key $nroboton in the hash %linkbot, with exists
if ( exists $linkbot{$nroboton} ) { ... } # warning if $nroboton undef
so altogether
if (defined $nroboton and exists $linkbot{$nroboton}) { ... }
and now you can check and work with the data in the arrayref, #{$linkbot{$nroboton}}.
Note that there is no need to double-quote that variable; it will be evaluated.

Matching optional query parameters using the variable name

I want to create a hash of optional query parameters that are sometimes passed to my subroutine. Sometimes a query parameter called welcome is passed in, and the value is either 1 or 0.
If that variable exists, I want to add it to a hash.
I've created a configuration value called OPTIONAL_URL_PARAMS which is a list of expected parameter names that can be passed in:
use constant OPTIONAL_URL_PARAMS => ("welcome")
So far I have:
my $tempParams = {};
if ( $optionalParam ) {
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
if ($optionalParam eq $self->{$param}) {
$tempParams->{$param} = $optionalParam;
$tempParams->{$param} =~ s/\s+//g; # strip whitespace
}
}
}
But this tries to use the value of $self->{$param} instead of its name. i.e. I want welcome to match welcome, but it's trying to match 1 instead.
I know when it comes to keys in a hash you can use keys %hash, but is there a way you can do this with regular variables?
Edit
My subroutine is being called indirectly:
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
If $optionalParam's variable name is 'welcome', then I want to try and map it to the constant welcome.
This is not an answer any more, but I cannot remove it yet as there is still a discussion going on to clarify the question.
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
# ...
}
The return value of OPTIONAL_URL_PARAMS (you already got an error here and that's why you have the &, that should have told you something...) is simply a list, not an array ref. Actually at this point it should throw an error because you cannot use 1 as an array reference.
Edit
In Perl, when you pass arguments to a subroutine, all the values are flattened into a single list (reference). Specifically, if you are passing parameters to a sub, the sub doesn't know the names of the variables you originally used. It only knows their values. Therefore, if you want names as well as values, you have to pass them separately. An easy way is using a hash. E.g., in new():
my $class = shift;
my $param = shift; # The first, required parameter
my %therest = (#_); # Optional parameters, if any
Then you can say URL::Handler->new($param, 'welcome' => 1), and $therest{welcome} will have the value 1. You can also say
URL::Handler->new($param, 'welcome'=>1, 'another'=>42);
and %therest will have the two keys welcome and another.
See also some further discussion of passing whole hashes as parameters
Original
This also probably doesn't answer the question!
Some thoughts on the code from your comment.
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift; # OK; refers to URL::Handler
my $args = #_; # Problematic: now $args is the _number_ of args passed (list #_ evaluated in scalar context).
my $self = {
# There is no %args hash, so the next two lines are not doing what you expect.
# I actually don't know enough perl to know what they do! :)
param => $args{param},
optionalParams => $args{optionalParam}
};
}
Some thoughts:
use strict; and use warnings; at the top of your source file, if you haven't yet.
I can think of no languages other than Algol 60 that support this idea. It goes against the idea of encapsulation, and prevents you from using an array or hash element, a function call, a constant, or an expression as the actual parameter to a call
Variable names are purely for the convenience of the programmer and have no part in the functionality of any running program. If you wished so, you could write your code using a single array #memory and have "variables" $memory[0], $memory[1] etc. But you would be bypassing the most useful part of compiler technology that allows us to relate readable text to memory locations. It is best to consider those names to be lost once the program is running
The called code should be interested only in the values passed, and it would be a nightmare if the name of a variable passed as an actual parameter were significant within the subroutine
If you were able to access the name of a variable passed as a parameter, what do you suppose would be provided to subroutine stats if the call looked like this
stats( ( $base[$i] + 0.6 + sum(#offset{qw/ x y /}) + sum(#aa) ) / #aa )
In summary, it cannot be done in general. If you need to associate a value with a name then you should probably be looking at hashes
Your code
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
has a number of problems
You correctly shift the name of the class from parameter array #_, but then set my $args = #_, which sets $args to the number of elements remaining in #_. But the value of $args is irrelevant because you never use it again
You then set $self to a new anonymous hash, which is created with two elements, using the values from hash %args. But %args doesn't exist, so the value of both elements will be undef. Had you put use strict and use warnings 'all' in place you would have been alerted to this
The keys that you're using to access this non-existent hash are param and optionalParam, and I think it's more than a coincidence that they match the names of the actual parameters of the call to new
While Perl is unusual in that it allows programmatic access to its symbol tables, it is an arcane and unrecommended method. Those names are essentially hidden from the program and the programmer and while modules like Exporter must manipulate symbol tables to do their job, any such behaviour inside base-level software is very much to be avoided
Finally, you never use $self again after defining it. You should be blessing it into a class according to the $class variable (which contains the string URL::Handler) and returning it from the constructor
I hope this helps

Perl If statement without parameters just plain variable is the EXP

I just wanted to know what does this code do?
my $string_1 = "foo bar";
my $val = 3;
if($string_1) {
}
basically what happen if you just use a variable inside an if statement?
thanks
It checks if the value of the variable is true. In Perl, everything is true but the following:
0 and the string '0'
undef
() (the empty list)
'' (an empty string)
This is documented in perlsyn. It also works with any other kind of value. You can also put a string, a function call inside the if condition. The behavior is always the same.
this will check variable '$string' has not null value

Why does this statment return true

Why does this statement,
if (! $ssh_options{user}) {
delete $ssh_options{user};
}
return true, but this statement
if ($ssh_options{user} eq 'undef') {
delete $ssh_options{user};
}
give me the error,
Use of uninitialized value $ssh_options{"user"} in string eq at analyze.pl line 230.
I thought you always had to have something for perl to compare the value of the variable being compared.
** Update **
# Quick Joe Smith
I cannot base my comparison on whether the hash keys exist or not because the values for this hash
sub ssh_connect {
my $host = shift;
my %ssh_options = (
port => shift,
user => shift,
password => shift
);
Come from this function which calls the sub routine
if ((exists $config_file{user}) && (exists $config_file{password})) {
my $vmware_user = $config_file{user};
my $vmware_password = $config_file{password};
ssh_connect($vmware_host, $vmware_port, $vmware_user, $vmware_password);
} else {
ssh_connect($vmware_host, $vmware_port);
}
Perl hash slots have a number of ways they can be false. If the key has never been filled, the exists function will return false. If the key exists but contains an undefined value, exists will be true but the defined function will return false. In normal boolean context, as given by an if statement, a hash key that was never filled, or is undefined, '', 0 or anything that reduces to those will be false.
From your update, it sounds like what you want is to use defined
delete $ssh_options{user} unless defined $ssh_options{user};
Use exists for checking hash keys.
if (exists $ssh_options{user}) {
...
}
However, in your case, it seems as though you're checking for existing keys whose values may be undefined, in which case:
if (defined $ssh_options{user}) {
...
}
The above may be what you want.
As a sidenote, checking to see if the value of any variable is defined in the way you described:
if ($something eq 'undef') {
...
}
Is wrong. You're checking to see if the variable contains the string "undef". What you need in those situations is:
unless (defined $something) {
...
}
But if $ssh_options{"user"} is uninitialized then it it doesn't have a value to compare to.
With Perl, undefined evaluates to false in Boolean context. So your first test 'works' as you expect. However, it might introduce a bug if zero or an empty string is a legitimate value for $ssh_options{user} to hold, if you don't wish to treat that the same as undef. That's because your first test doesn't differentiate between false as a value, and false as undefined. As far as Perl's Boolean evaluation goes, all of the following are "false": 0, '' (empty string), undef, or an empty list.
Your second snippet of code fails because undef should be a bareword, not a quoted string. 'undef' as a quoted string IS a value, which would actually evaluate to 'true' in Boolean tests. Because you're comparing an actual value to your hash element, Perl warns you that you're comparing something to an undefined value. In this case, the undefined value is $ssh_options{user}. It's actually good that Perl is warning you; it's given you a clue as to what you're doing wrong.
If you really want to test whether $ssh_options{user} is defined, use the defined() function. If you want to test whether $ssh_options{user} exists, use the exists() function.