What is the purpose of question mark(?) after a variable in Groovy - groovy

I am new to grails I found in many examples that a variable may end with question mark (?)
like this
boolean equals(other) {
if(other?.is(this))
return true
}
above code contains If condition in that other is ending with a ? so I want to know the meaning of that representation

?. is a null safe operator which is used to avoid unexpected NPE.
if ( a?.b ) { .. }
is same as
if ( a != null && a.b ) { .. }
But in this case is() is already null safe, so you would not need it
other.is( this )
should be good.

There is a subtlety of ?., the Safe navigation operator, not mentioned in #dmahapatro's answer.
Let me give an example:
def T = [test: true]
def F = [test: false]
def N = null
assert T?.test == true
assert F?.test == false
assert N?.test == null // not false!
In other words, a?.b is the same as a != null && a.b only when testing for a boolean value. The difference is that the first one can either evaluate to a.b or null, while the second one can only be a.b or false. This matters if the value of the expression is passed on to another expression.

Related

Terraform custom validation for variable that can be null

I'm trying to use Terraform 0.13's custom variable validation to ensure that the value is either null or between a specified range. I've tried every combination I can think of (using can, try, etc) but can't get it working for every case.
Here is the full non-working example:
variable "target_group_stickiness_duration" {
default = null
type = number
validation {
condition = (
var.target_group_stickiness_duration == null ||
( var.target_group_stickiness_duration >= 1 && var.target_group_stickiness_duration <= 604800 )
)
error_message = "Target group stickiness duration must be >= 1 and <= 604800."
}
}
This will fail if the value is null with the following error:
Error during operation: argument must not be null.
I believe this is due to eager evaluation of the conditional expression. Am I missing something obvious? Or any clever workarounds for this?
It fails becasue when your target_group_stickiness_duration is null, the
(var.target_group_stickiness_duration >= 1 && var.target_group_stickiness_duration <= 604800
)
is invalid.
You can try the following which uses coalesce:
variable "target_group_stickiness_duration" {
default = null
type = number
validation {
condition = (
var.target_group_stickiness_duration == null ||
(coalesce(var.target_group_stickiness_duration, 0) >= 1 && coalesce(var.target_group_stickiness_duration, 604801) <= 604800))
error_message = "Target group stickiness duration must be >= 1 and <= 604800."
}
}
It looks like there are two different things going on here.
The first is that tour condition expression includes the following subexpression:
var.target_group_stickiness_duration == null
This seems to be allowing the value to be null.
However, if we correct that to the opposite then it will expose a second problem:
condition = (
var.target_group_stickiness_duration != null &&
( var.target_group_stickiness_duration >= 1 && var.target_group_stickiness_duration <= 604800 )
)
Terraform's logical operators don't have the "short circuit" behavior in some other languages, where the result of the first operand can prevent any attempt to evaluate the second operand. Therefore both operands of && must be valid and successfully return either true or false.
The second operand (the nested && testing the range of the number) can't succeed if the value is null, because >= isn't defined for null numbers.
To make this work requires using a different strategy that avoids applying the >= operator to a null number. There are a few different ways to do that, including one with coalesce discussed in the accepted answer to this question.
Another alternative strategy is to use the conditional operator, which does allow the unselected result operand to fail, to allow us to use it as a guard for situations like this:
condition = (
var.target_group_stickiness_duration == null ? false : (
var.target_group_stickiness_duration >= 1 &&
var.target_group_stickiness_duration <= 604800
)
)
This is essentially an explicit implementation of the automatic short-circuit behavior some other languages offer in their logical operators. Terraform, at least in its current design, requires you to explicitly write out that the >= and <= operations are desirable only for non-null numbers.

Sum of nulls to return null

How to make
sum(array[*].value) to return NULL if all values are NULL or no values?
Currently it returns 0 in both cases
The best I can think of is
first_document(array[*].value) ? sum(array[*].value) : null
which needs to iterate collection twice
Is there a better way to do it?
How about using a FOR loop instead of the shorthand notation and filter out null values? Then we can check if zero elements remain and return null:
LET tmp = (FOR elem IN array FILTER elem.value != null RETURN elem)
RETURN LENGTH(tmp) ? SUM(tmp) : null
You could also use an array inline expression, but it's about the same amount of characters:
LET tmp = arr[* FILTER CURRENT.value != null RETURN CURRENT.value]
RETURN LENGTH(tmp) ? SUM(tmp) : null

How to use the `every` and the `any` Groovy methods?

I'm trying to understand how both methods every and any are working. I thought I had it but no. Here my example:
assert (["Test", "Case", "Way"].findAll {it ==~ /^r[a-z0-9]*/}.isEmpty()) == true
assert (["Test", "Case", "Way"].findAll {it ==~ /^r[a-z0-9]*/}.every { it.startsWith("intTest") }) == true
I'm always getting here true although none of the array elements is matching the pattern so none of them can start with "intTest".
There are no elements in your list.
Logically every returns true if the list is empty (as every element in the collection -- all 0 of them -- passes the test)
assert [].every { it == 1 } == true

Groovy Comparing two Strings

Why these two strings are not matching on neither one: .equals() OR .compareTo() OR == OR Objects.equals()? I believe it should match atleast at one comparions.
Process proc1 = 'cat /home/output.txt'.execute()
Process proc2 = 'grep -o -m 1 webServiceReturn'.execute()
Process all = proc1 | proc2
def log = all.text.toString()
String svc = "webServiceReturn";
println (log)
println (svc)
//if (svc.equals(log)) {
//if (svc.compareTo(log)) {
//if(svc == log) {
if (Objects.equals((svc),(log))) {
println "MATCHED" }
else {
println "NOT MATCHED" }
The result on all four comparison are:
webServiceReturn
webServiceReturn
NOT MATCHED
I was able to reproduce your issue and making one minor modification to trim the white space on the results allowed it to match.
Do a trim on your all.text to remove any extraneous white space.
def log = all.text.trim()
You don't need the .toString() call either.
I just learned from another StackOverflow answer that using equals(), .contains and in fail to see same contents of strings of different type.
Groovy different results on using equals() and == on a GStringImpl
According to an answer by #dunes in the above question, the Groovy comparison using == uses compareTo() opportunistically before equals(). (The == comparison in Groovy has a remarkable difference from Java where it compares references).
def expandedString = "${'test'}"
def simpleString = 'test'
println "Expansion: ${expandedString}"
println "equals 'test' ? ${expandedString.equals(simpleString)}"
println " == 'test' ? ${expandedString == simpleString}"
The above outputs the following,
Expansion: test
equals 'test' ? false
== 'test' ? true
The following excerpt from the Groovy documentation omits the compareTo() piece that makes == different from equals().
In Groovy, using == to test equality is different from using the same operator in Java. In Groovy, it is calling equals. If you want to compare reference equality, you should use is like in the following example:
def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']
def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']
assert list1 == list2
assert !list1.is(list2)
http://docs.groovy-lang.org/next/html/documentation/core-operators.html#_identity_operator

Find if any value in a list exists in another list

im a newbie in groovy so i have a question, i have two lists, and i want to know if a value that exists in the first list also exists in the second list, and it must return true or false.
I tried to make a short test but it doesn't works... here is what i tried:
// List 1
def modes = ["custom","not_specified","me2"]
// List 2
def modesConf = ["me1", "me2"]
// Bool
def test = false
test = modesConf.any { it =~ modes }
print test
but if i change the value of "me2" in the first array to "mex2" it returns true when it must return false
Any idea?
Simplest I can think of is using intersect and let Groovy truth kick in.
def modes = ["custom","not_specified","me2"]
def modesConf = ["me1", "me2"]
def otherList = ["mex1"]
assert modesConf.intersect(modes) //["me2"]
assert !otherList.intersect(modes) //[]
assert modesConf.intersect(modes) == ["me2"]
In case the assertion passed, you can get the common elements out of the intersection without doing a second operation. :)
I believe you want:
// List 1
def modes = ["custom","not_specified","me2"]
// List 2
def modesConf = ["me1", "me2"]
def test = modesConf.any { modes.contains( it ) }
print test
This disjoint() method returns true if there's no item that is common to both lists. It sounds like you want the negation of that:
def modes = ["custom","not_specified","me2"]
def modesConf = ["me1", "me2"]
assert modes.disjoint(modesConf) == false
modesConf = ["me1", "mex2"]
assert modes.disjoint(modesConf) == true
You can use any of the disjoint()/intersect()/any({}) which will return true/false. Below given are the examples:
def list1=[1,2,3]
def list2=[3,4,5]
list1.disjoint(list2) // true means there is no common elements false means there is/are
list1.any{list2.contains(it)} //true means there are common elements
list1.intersect(list2) //[] empty list means there is no common element.
def missingItem = modesConf.find { !modes.contains(it) }
assert missingFile == "me1"
In such case, missingItem will contain a missing element which exists in modesConf but doesn't exist in modes. Or will be null if everything is fine.

Resources