Today I stumbled with what it looked like a little Groovy magic when I found out I could get a property from a List of Maps using the GroovyBeans dot notation:
List list = [[a: 1, b: 2], [c: 3, d: 4]]
assert list.b == [2, null]
This will be great for my use case if the value returned was, in the example above, 2 only. I thought that to override the getAt method would do the trick, and in fact this works (in this example if there is more than 1 non null result it will return the first which is OK)
List.metaClass.getAt = { String s -> return delegate.findAll({ Map m -> m.get(s) }).get(0).get(s) }
List list = [[a: 1, b: 2], [c: 3, d: 4]]
assert list.getAt("b") == 2
but... if I use the dot notation the method being invoked is not the getAt(String)
List.metaClass.getAt = { String s -> return delegate.findAll({ Map m -> m.get(s) }).get(0).get(s) }
List list = [[a: 1, b: 2], [c: 3, d: 4]]
assert list.b == 2 // Assertion failed
I couldn't find what method is being invoked with the dot notation... Anybody have any clues?
You may wonder why I want to use the dot notation when calling the method explicitly gives me what I want, but in fact this is part of a DSL I'm writing so I can easily explain to people who are going to use it what is for instance
current.processDate
but it's hard to explain they have to do
current[0].processDate ?: current[1].processDate
or even
current.getAt("processDate")
Thanks in advance.
UPDATE
I now think think it is impossible to do so. I stepped thru all the code I could, the piece of code that actually get values for the maps in the list and accumulates those values is
DefaultGroovyMethods
private static List getAtIterable(Iterable coll, String property, List<Object> answer)
and I can't override it like this
DefaultGroovyMethods.metaClass.static.getAtIterable = {
coll, property, answer ->
def find = coll.find { it != null }
return find
}
I also tried to override the very first method called in the dot notation
AbstractCallSite
public Object callGetProperty(Object receiver) throws Throwable {
return this.acceptGetProperty(receiver).getProperty(receiver);
}
like this:
AbstractCallSite.metaClass.callGetProperty = {
Object receiver ->
if ( !(receiver instanceof ArrayList)){
return delegate.callGetProperty(receiver)
}
return "SOMETHING"
}
So maybe it's time to call it quits?
Related
I have a collection which I process with removeIf {} in Groovy. Inside the block, I have access to some it identifier. What is this and where is it documented?
it is an implicit variable that is provided in closures. It's available when the closure doesn't have an explicitly declared parameter.
When the closure is used with collection methods, such as removeIf, it will point to the current iteration item.
It's like you declared this:
List<Integer> integers = [1, 2, 3]
for(Integer it: integers) {print(it)}
When you use each, instead (and that's an example), you can get it implicitly provided:
integers.each{print(it)} //it is given by default
Or
integers.removeIf{it % 2 == 0} //it is the argument to Predicate.test()
it will successively take the values 1, 2, and 3 as iterations go.
You can, of course, rename the variable by declaring the parameter in the closure:
integers.each{myInteger -> print(myInteger)}
In this case, Groovy doesn't supply the implicit it variable. The documentation has more details
If you create a closure without an explicit argument list, it defaults to having a single argument named it. Here's an example that can be run in the Groovy console
Closure incrementBy4 = { it + 4 }
// test it
assert incrementBy4(6) == 10
In the example above the closure is identical to
Closure incrementBy4 = { it -> it + 4 }
Here's another example that uses removeIf
Closure remove2 = { it == 2 }
def numbers = [1, 2, 3]
numbers.removeIf(remove2)
// verify that it worked as expected
assert numbers == [1, 2]
how sort works on list of lists when spaceship operator (compareTo) fails for list?
Note that it's not just equals, but compareTo (greater ,lower, or equal)
def v1=[1, 2, 0]
def v2=[1, 3, 0]
def v=[v2,v1]
println v
//prints> [[1, 3, 0], [1, 2, 0]]
v=v.sort()
assert v[0]==v1
println v
//prints> [[1, 2, 0], [1, 3, 0]]
c=c.sort{x,y-> y<=>x}
//throws Cannot compare java.util.ArrayList with value '[1, 3]' and java.util.ArrayList with value '[1, 2, 3]'
According to code above List<List>.sort() works.
According to Iterable.sort() documentation:
Sorts the Collection. Assumes that the collection items are comparable and uses their natural ordering to determine the resulting order.
The question why and how List<List>.sort() works in groovy?
Can somebody point me how comparison of arrays implemented in groovy for sort() operation?
The reason for the different behaviour, is because different methods are involved. When you call c.sort{x,y-> y<=>x}, this invokes Iterable.sort, which throws an exception because the elements of the list are not Comparable.
However, when you call v.sort(), you're invoking this method defined in DefaultGroovyMethods.java
public static <T> List<T> sort(Iterable<T> self) {
return sort(self, true);
}
which invokes this:
public static <T> List<T> sort(Iterable<T> self, boolean mutate) {
List<T> answer = mutate ? asList(self) : toList(self);
Collections.sort(answer, new NumberAwareComparator<T>());
return answer;
}
as you can see this provides a Comparator to determine the ordering of the list elements, so they don't need to be Comparable themselves.
Given class Foo with this decidedly poor hashCode() implementation:
class Foo {
String name
public int hashCode() {
0
}
public boolean equals(Object obj) {
if (obj == null) {
return false
}
if (!(obj instanceof Foo)) {
return false
}
Foo foo = (Foo) obj
return this.name.equals(foo.name)
}
}
Why does the following assertion fail?
Foo f1 = new Foo(name: 'Name 1')
Foo f2 = new Foo(name: 'Name 2')
Foo f3 = new Foo(name: 'Name 2')
assert ([f1, f2] - [f3]).size() == 1
The result of the minus() is an empty list. If I switch the hashCode() implementaion toreturn name.hashCode(), the assertion passes. With either implementation, methods like contains() work as expected.
My question is not how to implement a better hashCode(), but why minus() behaves this way.
this would be exactly the behaviour described in the docs for minus:
Create a List composed of the elements of the first list minus every occurrence of elements of the given Collection.
assert [1, "a", true, true, false, 5.3] - [true, 5.3] == [1, "a", false]
You remove each element, that is in the second list. In your case remove all f3 from [f1,f2], where all are the same, hence the empty list.
The finer details are in DefaultGroovyMethods.minus and then in NumberAwareComperator, which uses the hashCode. as you have already found, there are open tickets regarding this (https://jira.codehaus.org/browse/GROOVY-7158). So under the eyes, that hashCode is used there, the behaviour is perfectly consistent... should it be used there? maybe not, because there are cases, where it really gets odd (e.g. [[x:0,y:0]]-[[x:1,y:1]]==[]).
The case [f1,f2]-f3 takes another route in code and therefor behaves differently.
For now my best guess would be, that you use minus for immutable types (like above example), where it works quite well. Beside that, rather work with sets.
The java Collections use the implementation of hashCode/equals to determine object equality. Your implementation of hashCode indicates that that f1, f2, and f3 are all "the same". Loosely speaking:
[f1, f2] - [f3]
could be read as
Remove from the list all objects that are the same as f3
so it removes all objects.
You seem to already be aware that this is a terrible way to implement hashCode, so it's really just a case of "garbage in, garbage out".
I have a list of objects, let it be integers:
list = [1, 3, 8]
I need a list of objects which in fact are PROVIDERS of these integers. I.e.:
providersList = [p1.getInteger() = 1, p2.getInteger() = 3, p3.getInteger() = 8] //pseudo code
How can I code it?
UPDATE
Sorry, looks like I was a little bit confusing. This is one of my first experiences with Spock so I may confuse the terms and ideas.
I want to mock a list of objects. Each object has its own methods, of course. The test will call only one method of that object. I dont need to implement it because I know what exactly returns this method. And these return values are stored in some list which I already have.
I.e. I need to mock a list of objects [p1, p2, p3] where each call to object.getInteger() returns some integer I already know, and I have a list of these integers: [i1, i2, i3]: p1.getInteger() = i1 etc.
How can I mock list of [p1, p2, p3] with the help of list [i1, i2, i3] ?
You may just try:
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class Test extends Specification {
def 'some spec'() {
given:
def mocks = [1, 2, 3].collect { i -> GroovyMock(SomeClass) {
getInteger() >> i
}
}
expect:
mocks*.getInteger().containsAll([1,2,3])
}
}
class SomeClass {
Integer getInteger() {
0
}
}
is there a way to 'break' out of a groovy closure.
maybe something like this:
[1, 2, 3].each {
println(it)
if (it == 2)
break
}
I often forget that Groovy implements an "any" method.
[1, 2, 3].any
{
println it
return (it == 2)
}
12/05/2013 Heavily Edited.
Answering the question that was asked.
Is it possible to break out of a Closure?
You would "break" out of a closure by issuing the return keyword. However that isn't helpful in the example that is given. The reason for this is that the closure (think of it as a method) is called by the each method for every item in the collection.
If you run this example you will see it will print 1 then 3.
[1, 2, 3].each {
if (it == 2) return
println(it)
}
Why break in the context of each doesn't make sense.
To understand why you cannot break out of the each method like you could break out of a for loop you need to understand a bit of what is actually happening. Here is a gross simplification what the each method on a collection does.
myEach([0,1,3])
void myEach(List things) {
for (i in things) {
myEachMethod(i)
}
}
void myEachMethod(Object it) { // this is your Closure
if(it == 2) return
println it
}
As you can see the closure is basically a method that can be passed around. Just as in java you cannot break from within method call or closure.
What to do instead of breaking from each.
In Groovy you are supposed to express your code using high level abstractions as such primitive looping is not idiomatic. For the example that you gave I would consider making use of findAll. For example:
[1,2,3].findAll { it < 2 }.each { println it }
I hope this helps you understand what is going on.
Answering the implied question.
Can you break out of the Collection.each iterations against your supplied closure?
You cannot break out of the each method without throwing and catching an exception as John Wagenleitner has said. Although I would argue that throwing and catching an exception in the name of flow control is a code smell and a fellow programmer might slap your hands.
You can throw an exception:
try {
[1, 2, 3].each {
println(it)
if (it == 2)
throw new Exception("return from closure")
}
} catch (Exception e) { }
Use could also use "findAll" or "grep" to filter out your list and then use "each".
[1, 2, 3].findAll{ it < 3 }.each{ println it }
Take a look at Best pattern for simulating continue in groovy closure for an extensive discussion.
Try to use any instead of each
def list = [1, 2, 3, 4, 5, -1, -2]
list.any { element ->
if (element > 3)
return true // break
println element
}
The result : 1, 2, 3
Just using special Closure
// declare and implement:
def eachWithBreak = { list, Closure c ->
boolean bBreak = false
list.each() { it ->
if (bBreak) return
bBreak = c(it)
}
}
def list = [1,2,3,4,5,6]
eachWithBreak list, { it ->
if (it > 3) return true // break 'eachWithBreak'
println it
return false // next it
}
There is an other solution. Although, that groovy stuff like each/find/any is quite cool: if it doesn't fit, don't use it. You can still use the plain old
for (def element : list)
Especially, if you want to leave the method, too. Now you are free to use continue/break/return as you like. The resulting code might not be cool, but it is easy and understandable.
This is in support of John Wagenleiter's answer. Tigerizzy's answer is plain wrong. It can easily be disproved practically by executing his first code sample, or theoretically by reading Groovy documentation. A return returns a value (or null without an argument) from the current iteration, but does not stop the iteration. In a closure it behaves rather like continue.
You won't be able to use inject without understanding this.
There is no way to 'break the loop' except by throwing an exception. Using exceptions for this purpose is considered smelly. So, just as Wagenleiter suggests, the best practice is to filter out the elements you want to iterate over before launching each or one of its cousins.
With rx-java you can transform an iterable in to an observable.
Then you can replace continue with a filter and break with takeWhile
Here is an example:
import rx.Observable
Observable.from(1..100000000000000000)
.filter { it % 2 != 1}
.takeWhile { it<10 }
.forEach {println it}