I know his is sort of a duplicate of Is if(pointerVar) the same as if(pointerVar!=NULL)?, but I have to ask anyway.
In Groovy, we have the following:
def x = someMethod()
if( !x ) {
// do good stuff
}
This is just a standard null check, ie (x != null), right?
No. if in groovy calls the underlying asBoolean() method. This is known as Groovy truth.
Empty lists, empty strings, empty maps, null, 0, are all falsy values:
if ([:]) {
assert false
}
if (null) {
assert false
}
if ("") {
assert false
}
if (0) {
assert false
}
assert null.asBoolean() == false
assert 1.asBoolean()
You can also write asBoolean in your own classes.
Related
I'm writing a test using the Spock framework and I found a strange bavior when testing equality of lists.
When I compare two lists like
sourceList == targetList
and those lists contains Comparable objects of the same type, those objects are tested for equality using its compareTo methods instead of equals.
Is there any simple way how to force Groovy to use equals when testing equality on such lists?
Here is a simple test specification where the test should fail, but it does not.
class Test extends Specification {
def "list test"() {
when:
def listA = [[index: 1, text: "1"] as Bean, [index: 2, text: "2"] as Bean]
def listB = [[index: 1, text: "1"] as Bean, [index: 2, text: "3"] as Bean]
then:
listA == listB
}
class Bean implements Comparable<Bean> {
int index
String text
#Override
public int compareTo(Bean o) {
return index.compareTo(o.index);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + index;
result = prime * result + ((text == null) ? 0 : text.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Bean)) {
return false;
}
Bean other = (Bean) obj;
if (index != other.index) {
return false;
}
if (text == null) {
if (other.text != null) {
return false;
}
} else if (!text.equals(other.text)) {
return false;
}
return true;
}
}
}
Asking for a simple method is subjective. The key to this answer is that, in Groovy, the is() method will check object equality.
Consider this new method on ArrayList. If appropriate, it will zip together the two lists and call is() on each pair of items.
ArrayList.metaClass.myCompare = { listB ->
def result = false
def listA = delegate
if (listB && listA.size() == listB.size()) {
def zipped = [listA, listB].transpose()
result = zipped.inject(true){ ok, pair ->
ok && pair[0].is(pair[1])
}
}
result
}
then:
def beanA = new Bean(index: 1, text: 'a')
def beanA1 = new Bean(index: 1, text: 'a')
def beanB = new Bean(index: 2, text: 'b')
assert [beanA, beanB].myCompare([beanA, beanB])
assert ! [beanA, beanB].myCompare([beanA1, beanB])
assert ! [beanA, beanB].myCompare([beanA])
assert ! [beanA, beanB].myCompare(null)
assert ! [beanA, beanB].myCompare([])
In groovy, "==" is replaced with equals if items are not comparable, or with compareTo if they implement Comparable. I do not know a simple way to force equals, if the collections have no duplicates, you can check the elements of the collection one by one:
def collectionsEqual (Collection<?> collection1, Collection<?> collection2) {
if (collection1.size() != collection2.size()) {
return false
}
for (elem1 in collection1) {
def founded = false;
for (elem2 in collection2) {
if (elem1?.equals (elem2)) {
founded = true
}
}
if (!founded) {
return false
}
}
true
}
but, in https://www.baeldung.com/java-compareto we have:
It is also strongly recommended, though not required, to keep the
compareTo implementation consistent with the equals method
implementation:
x.compareTo(y) == 0 should have the same boolean value as x.equals(y)
This will ensure that we can safely use objects in sorted sets and
sorted maps.
Working on some translation / mapping functionality using Maps/JsonBuilder in Groovy.
Is is possible (without creating extra code outside of the map literal creation) .. to conditionally include/exclude certain key/value pairs ? Some thing along the lines of the following ..
def someConditional = true
def mapResult =
[
"id":123,
"somethingElse":[],
if(someConditional){ return ["onlyIfConditionalTrue":true]}
]
Expected results:
If someConditional if false, only 2 key/value pairs will exist in mapResult.
If someConditional if true, all 3 key/value pairs will exist.
Note that I'm sure it could be done if I create methods / and split things up.. for to keep things concise I would want to keep things inside of the map creation.
You can help yourself with with:
[a:1, b:2].with{
if (false) {
c = 1
}
it
}
With a small helper:
Map newMap(m=[:], Closure c) {
m.with c
m
}
E.g.:
def m = newMap {
a = 1
b = 1
if (true) {
c = 1
}
if (false) {
d = 1
}
}
assert m.a == 1
assert m.b == 1
assert m.c == 1
assert !m.containsKey('d')
Or pass an initial map:
newMap(a:1, b:2) {
if (true) {
c = 1
}
if (false) {
d = 1
}
}
edit
Since Groovy 2.5, there is an alternative for with called tap. It
works like with but does not return the return value from the closure,
but the delegate. So this can be written as:
[a:1, b:2].tap{
if (false) {
c = 1
}
}
You could potentially map all false conditions to a common key (e.g. "/dev/null", "", etc) and then remove that key afterwards as part of a contract. Consider the following:
def condA = true
def condB = false
def condC = false
def mapResult =
[
"id":123,
"somethingElse":[],
(condA ? "condA" : "") : "hello",
(condB ? "condB" : "") : "abc",
(condB ? "condC" : "") : "ijk",
]
// mandatory, arguably reasonable
mapResult.remove("")
assert 3 == mapResult.keySet().size()
assert 123 == mapResult["id"]
assert [] == mapResult["somethingElse"]
assert "hello" == mapResult["condA"]
There is no such syntax, the best you can do is
def someConditional = true
def mapResult = [
"id":123,
"somethingElse":[]
]
if (someConditional) {
mapResult.onlyIfConditionalTrue = true
}
I agree with Donal, without code outside of map creation it is difficult.
At least you would have to implement your own ConditionalMap, it is a little work but perfectly doable.
Each element could have it's own condition like
map["a"] = "A"
map["b"] = "B"
map.put("c","C", true)
map.put("d","D", { myCondition })
etc...
Here an incomplete example (I did only put, get, keySet, values and size to illustrate, and not typed - but you probably don't need types here?), you will probably have to implement few others (isEmpty, containsKey etc...).
class ConditionalMap extends HashMap {
/** Default condition can be a closure */
def defaultCondition = true
/** Put an elemtn with default condition */
def put(key, value) {
super.put(key, new Tuple(defaultCondition, value))
}
/** Put an elemetn with specific condition */
def put(key, value, condition) {
super.put(key, new Tuple(condition, value))
}
/** Get visible element only */
def get(key) {
def tuple = super.get(key)
tuple[0] == true ? tuple[1] : null
}
/** Not part of Map , just to know the real size*/
def int realSize() {
super.keySet().size()
}
/** Includes only the "visible" elements keys */
def Set keySet() {
super.keySet().inject(new HashSet(),
{ result, key
->
def tuple = super.get(key)
if (tuple[0])
result.add(key)
result
})
}
/** Includes only the "visible" elements keys */
def Collection values() {
this.keySet().asCollection().collect({ k -> this[k] })
}
/** Includes only the "visible" elements keys */
def int size() {
this.keySet().size()
}
}
/** default condition that do not accept elements */
def map = new ConditionalMap(defaultCondition: false)
/** condition can be a closure too */
// def map = new ConditionalMap(defaultCondition : {-> true == false })
map["a"] = "A"
map["b"] = "B"
map.put("c","C", true)
map.put("d","D", false)
assert map.size() == 1
assert map.realSize() == 4
println map["a"]
println map["b"]
println map["c"]
println map["d"]
println "size: ${map.size()}"
println "realSize: ${map.realSize()}"
println "keySet: ${map.keySet()}"
println "values: ${map.values()}"
/** end of script */
You can use the spread operator to do this for for both maps and lists:
def t = true
def map = [
a:5,
*:(t ? [b:6] : [:])
]
println(map)
[a:5, b:6]
This works in v3, haven't tried in prior versions.
How can I check if a list contains a key or value inside a sublist (of sublist ..) or in the "root"
And is possible to get a the "path"?
This containsValue or containsKey seems only to look in the root of the list
Example pseudo:
//This is my list
list = [languages:[_clazz:"basics.i18n.Language", messages:[_clazz:"basics.i18n.Message"]]]
list.containsKey("languages") // return true
list.containsValue("basics.i18n.Language") // return false where I want true
list.containsKey("messages") // return false // return false where I want true
list.containsValue("basics.i18n.Message") // return false where I want true
There's nothing in Groovy for this I don't think, but you can write your own and add them to Map (what you have is a Map, not a List as you have named the variable)
Map.metaClass.deepContainsKey = { value ->
delegate.containsKey(value) ?:
delegate.values().findAll { it instanceof Map }.findResult { it.deepContainsKey(value) } ?:
false
}
Map.metaClass.deepContainsValue = { value ->
delegate.containsValue(value) ?:
delegate.values().findAll { it instanceof Map }.findResult { it.deepContainsValue(value) } ?:
false
}
Then, given your map:
def map = [languages:[_clazz:"basics.i18n.Language", messages:[_clazz:"basics.i18n.Message"]]]
All of these assertions pass:
assert map.deepContainsKey("messages")
assert map.deepContainsValue("basics.i18n.Message")
assert map.deepContainsValue("basics.i18n.Language")
assert !map.deepContainsKey("missing")
assert !map.deepContainsValue("novalue")
I have a utility method that needs to check if a Map<String,Integer> contains entries (keys) whose values are all zero (0):
def mapIsAllZeros(Map<String,Integer> toCheck) {
println toCheck
toCheck.each {
if(it.value != 0) {
return false
} else {
println "Value is : " + it.value
}
}
println "Returning true..."
true
}
Hence:
Map<String,Integer> m1 = new HashMap<String,Integer>()
m1.put("fizz", 0)
m1.put("buzz", 0)
Map<String,Integer> m2 = new HashMap<String,Integer>()
m2.put("fizz", 0)
m2.put("buzz", 1)
boolean m1Check = mapIsAllZeros(m1) // TRUE
boolean m2Check = mapIsAllZeros(m2) // FALSE
However when I run this method passing it m2 I get:
[fizz:0, buzz:1]
Value is : 0
Value is : 0
Returning true...
What's going on?
each can not return. use find instead. also this can be done in one line: [fizz:0, buzz:1].every{!it.value}
def mapIsAllZeros(Map<String,Integer> toCheck) {
return toCheck.values().every{it==0}
}
assert !mapIsAllZeros([fizz: 0, buzz: 1])
assert !mapIsAllZeros([fizz: 1, buzz: 1])
assert mapIsAllZeros([fizz: 0, buzz: 0])
assert !mapIsAllZeros([fizz: null, buzz: null])
Edit: thanks to #dmahapatro: as the methods's name suggest a proper check against zero, let's not work with groovy-truth, so null:s wont result into something misleading
The problem here is that the return statement returns from the each closure, not from the mapIsAllZeros method. I.e. the closure returns false and then each continues the iteration.
So you should use something other than each. For instance, using find:
boolean mapIsAllZeros(Map<String, Integer> toCheck) {
!(toCheck.find { it.value != 0 })
}
Another alternative is to use a for loop:
boolean mapIsAllZeros(Map<String, Integer> toCheck) {
for (entry in toCheck.entrySet()) {
if (entry.value != 0)
return false
}
return true
}
The problem with returning from each closure was already clarified well, nevertheless You can also use the following piece of code (method name borrowed from #Steinar)
boolean mapIsAllZeros(Map<String, Integer> toCheck) {
!toCheck.values().contains(0)
}
Simple and uses API that is well known.
A solution comparing unique values:
def mapIsAllZeros(map) {
map*.value.unique() == [0]
}
assert !mapIsAllZeros([fizz: 0, buzz: 1])
assert !mapIsAllZeros([fizz: 1, buzz: 1])
assert mapIsAllZeros([fizz: 0, buzz: 0])
Because there are not enough answers here.
I'm looking for a succinct expression for evaluating whether or not all the items in a list are either Longs or may be converted to Longs. I'm looking for a compact expression, but I prefer readability over mere code golf.
Examples:
assert expr([1,2,'3'])
assert expr([1,'fish',3]) == false
assert expr([null]) == false
assert expr([' ', 17]) == false
For readability, I would do something like:
def canBeLongs(things) {
things.every {
try { Long.valueOf(it); true }
catch (e) { false }
}
}
assert canBeLongs([1,2,'3'])
assert !canBeLongs([1,'fish',3])
assert !canBeLongs([null])
assert !canBeLongs([' ', 17])
Just for the lulz, this other solution is horrible, but it's very succinct and covers all the test cases:
def f(l) { l.join() ==~ /[0-9]*/ }
assert f([1,2,'3'])
assert !f([1,'fish',3])
assert !f([null])
assert !f([' ', 17])
The shortest I can get on the first go is:
def expr = { a ->
a.inject([]) { c, v ->
c << { try{ Long.valueOf(it); true } catch(e){ false } }( v )
}.every()
}
Edit
#epidemian has the best answer, which can be made shorter by moving the try outside the loop
def canBeLongs(things) {
try { things.every { Long.valueOf(it) } }
catch(e) { false }
}
There's actually a built in String.isLong() method from Groovy, so another (terrible) answer that is succinct is:
def canBeLongs(things) { things.every{ "$it".long } }
assert canBeLongs([1,2,'3'])
assert canBeLongs([1,'fish',3]) == false
assert canBeLongs([null]) == false
assert canBeLongs([' ', 17]) == false
I only include it because it's so short.