Groovy here. I have a class Fizz:
#Canonical
class Fizz {
Integer id
String name
}
In my program, I compose a map of them by their integral id field:
// Here, each key of the map corresponds to the Fizz#id of its value
// example:
// allFizzes[1] = new Fizz(1, 'Foobar')
// allFizzes[3004] = new Fizz(3004, 'Wakka wakka')
Map<Integer,Fizz> allFizzes = getSomehow()
I would know like to obtain a set of "bad" Fizzes whose name equals the string 'Sampson'. My best attempt:
Set<Fizz> badFizzes = allFizzes.find { k,v -> v.equals('Sampson') }
However this gives me runtime errors:
Exception in thread "main" org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '24={
"id": 24,
"name": "Sampson"
},
' with class 'java.util.LinkedHashMap$Entry' to class 'java.util.Set'
So it looks like even though I'm specifying v.equals('Sampson') that Groovy is strill trying to save a Map key,value pair into a Set. What's the Grooviest solution here?
You'll want to use findAll (which returns a Collection) in place of find (which returns an Object). findAll applies the passed in closure to each EntrySet in the Map and returns a new collection with elements that satisfied your closure's criteria. You can then call values() on this newly returned collection.
Map<String, Integer> myMap = ["fizzStrength":29, "fizzQuality": 123, "fizzFizziness":3]
Set<Integer> found = myMap.findAll { k,v -> v > 4 }.values() // returns [29, 123]
Related
can I combine below closures into one or do this in a more functional and elegant way in groovy. I am using the sortMethod in some other places( for testing purpose) too.
for eg : countAndMap should take
["a b c a a c" , "b b c"] and return[x1 : [a:3,c:2,b:1] , x2 : [b:2,c:1]]
def countAndMap(List<String> stringList) {
stringList.withIndex().collect { String s, Integer i -> [(num.call(i)): count.call(s)] }
}
Closure count = {sortMethod.call(it.split().countBy {it}) }
Closure sortMethod = { it.sort { x, y -> x.value <=> y.value } }
Closure num = { "x ${it + 1}".toString()}
there are no errors but I wonder if it's possible to do it in a more functional way
I am not sure what you mean with "more functional", but you could use a fold operation (called inject in groovy):
list = ["a b c a a c" , "b b c"]
def createSortedHistogram(String toCount) {
toCount
.split() // Create list of words
.inject([:]){ acc, word -> acc[word] = 1 + (acc[word] ?: 0);acc} // create histogram
.sort{-it.value} // sort histogram map by value desc
}
def countAndMap(List<String> list) {
list.withIndex().collectEntries{ sublist, i -> ["x ${i+1}": createSortedHistogram(sublist)] }
}
countAndMap(list)
I think the most interesting part is the inject method.
This solution uses the initial value [:] in order to use a map as result. In each iteration the inject operation either adds a new entry with value 1 to the map (if the word/key does not exist in the map) or increases the value of the word/key if it is already present in the map.
See the inject definition from Collections GroovyDoc.
public Object inject(Object initialValue, Closure closure) - Iterates through the given Collection, passing in the initial value to the 2-arg closure along with the first item. The result is passed back (injected) into the closure along with the second item. The new result is injected back into the closure along with the third item and so on until the entire collection has been used. Also known as foldLeft or reduce in functional parlance.
Is there a way to instantiate the value of map lazy?
For example
class MapTest {
#Lazy(soft = true) HashMap<String, List<String>> map
}
Doing like this I can use this call and get null without recieving NullPointerException
new MapTest().map.key1
However attempt to call
map.key1.remove(1)
will lead to NullPointerException due the value being null. (it would be fine if it threw IndexOutOfBounds exception)
Is there a way to instantiate the list value of the map?
try map.withDefault :
def map = [:].withDefault { [] }
assert map.key1.isEmpty()
Some explanation :
[:] is the groovy way to instantiate an empty hash map
withDefault is a groovy method on a map wich take a closure. this closure is call every time a key is requested to initialize the value if it doesn't exist. this closure take one parameter (the key) and should the value
[] is the groovy way to create an empty list - { [] } is a closure wich return an empty list for every key
see others examples here
I'm trying to use the Groovy way of creating a TreeMap<String, List<Data>> with default values so I easily add data to a new list if the key isn't already present.
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }
As you can see, I have the requirement to use a TreeMap and withDefault only returns a Map instance, so I need to cast.
When I attempt to add a new list to the map,
myData[newKey].add(newData)
myData[newKey] is null. However, if I change my Map initilization to remove the TreeMap cast (and change the type to just Map instead of TreeMap), myData[newKey].add(newData) works as expected.
What's the reasoning for this? Can I not use withDefault if I cast the map?
The problem isn't just about the cast. It also has to do with the declared type. The problem can be simplified to something like this:
def map1 = [:].withDefault { 0 }
TreeMap map2 = map1
When that is executed map1 is an instance of groovy.lang.MapWithDefault and map2 is an instance of java.util.TreeMap. They are 2 separate objects on the heap, not just 2 references pointing to the same object. map2 will not have any default behavior associated with it. It is as if you had done this:
def map1 = [:].withDefault { 0 }
TreeMap map2 = new TreeMap(map1)
That is what is happening with your code. The cast and the generics just makes it less clear with your code.
This:
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }
Can be broken down to this:
def tmpMap = [:].withDefault { [] }
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>)tmpMap
I hope that helps.
EDIT:
Another way to see the same thing happening is to do something like this:
Set names = new HashSet()
ArrayList namesList = names
When the second line executes a new ArrayList is created as if you had done ArrayList namesList = new ArrayList(names). That looks different than what you have in your code, but the same sort of thing is happening. You have a reference with a static type associated with it and are pointing that reference at an object of a different type and Groovy is creating an instance of your declared type. In this simple example above, that declared type is ArrayList. In your example that declared type is TreeMap<String, List<Data>>.
I'm trying to write a mini DSL for some specific task. For this purpose I've been trying to solve a problem like this below (without using parantheses):
give me 5 like romanLetter
give me 5 like word
where the first line would return "V" and the second "five"
My definitions for the first part give me 5 look like this
def give = { clos -> clos() }
def me = { clos -> [:].withDefault { it
println it}
}
and then give me 5 prints 5
The problem is how to add more metaclass methods on the right. E.g.
give me 5 like romanLetter -> prints V OR
give me 5 like word -> prints five
my intuition is that I define like as
Object.metaClass.like = {orth -> if (orth.equals("roman")){ println "V"}
else {println "five"} }
this metaClass method like works only if there is a returned value from the left to be applied to, right? I tried adding a return statement in all of the closures which are on the left side but I always receive
groovy.lang.MissingPropertyException: No such property: like
for class: com.ontotext.paces.rules.FERulesScriptTest ...
do you have an idea how shall I do?
========================================
Here is the application of what I'm asking for.
I want to make a rule as follows
add FEATURE of X opts A,B,C named Y
where add is a closure, of, opts and named are MetaClass methods (at least that's how i imagine it), X, A, B, C, Y are parameters most probably strings and FEATURE is either a MetaClass property, or a closure without arguments or a closure with arguments.
If FEATURE does not take arguments then it is enough that add takes FEATURE as argument and returns a value on which
Object.metaClass.of will be executed with parameter X
Object.metaClass.opts will be executed on the returned by OF value with parameters A, B, C
Object.metaClass.named will be executed on the returned by opts value with parameter Y
each one of these metaclass methods sets its parameter as a value in a map, which is passed to a JAVA method when named is called.
I'm not sure this is the best solution for such a problem, but it seems to me such for the moment. The problem is if FEATURE is not a property itself but a closure which takes argument (e.g. feature1 ARG1). Then
add feature1 ARG1 of X opts A,B,C named Y
and this is the case which I'm stuck with. add feature1 ARG1 is the give me 5 part and I'm trying to add the rest to it.
========================================================
EXAMPLES:
I need to have both of the following working:
add contextFeature "text" of 1,2,3 opts "upperCase" named "TO_UPPER"
add length named "LENGTH"
where in the first case by parsing the rule, whenever each metaclass method of, opts, named is called I fill in the corresponding value in the following map:
params = [feature: "text",
of: 1,2,3,
opts: "upperCase",
named: "TO_UPPER"]
ones this map is filled in, which happens when named is parsed, I call a java method
setFeature(params.of, params.named, params.opts, params.feature)
In the second case length is predefined as length = "length", params values will be only
params = [feature : length,
of: null,
opts: null,
named: "LENGTH"]
and since of is null another java method will be called which is addSurfaceFeature(params.feature, params.named). The second case is more or less streight forward, but the first one is the one I can't manage.
Thanks in advance! Iv
You can do this sort of thing... Does that get you close?
def contextFeature( type ) {
"FEATURE_$type"
}
// Testing
new IvitaParser().parse {
a = add text of 1,2,3 opts "upperCase" named "TO_UPPER"
b = add length named "LENGTH"
c = add contextFeature( "text" ) of 1,2,3 opts "upperCase" named "TO_UPPER"
}
assert a == [feature:'text', of:[1, 2, 3], opts:'upperCase', named:'TO_UPPER']
assert b == [feature:'length', of:null, opts:null, named:'LENGTH']
assert c == [feature:'FEATURE_text', of:[1, 2, 3], opts:'upperCase', named:'TO_UPPER']
// Implementation
class IvitaParser {
Map result
def parse( Closure c ) {
c.delegate = this
c.resolveMethod = Closure.DELEGATE_FIRST
c()
}
def propertyMissing( String name ) {
name
}
def add( String param ) {
result = [ feature:param, of:null, opts:null, named:null ]
this
}
def of( Object... values ) {
result.of = values
this
}
def named( String name ) {
result.named = name
result
}
def opts( String opt ) {
result.opts = opt
this
}
}
You can even get rid of the quotes on the definition:
a = add text of 1,2,3 opts upperCase named TO_UPPER
b = add length named LENGTH
As the propertyMissing method just converts unknown properties into a String of their name
When using a map of closures to implement an interface in Groovy (as in http://groovy.codehaus.org/Groovy+way+to+implement+interfaces) is there any way to convert the object back to a map after using the as keyword or the asType method to implement the interface?
Based on your use case it would seem that you could just keep a reference to the original Map before converting it into the needed interface.
However, looking at the source code that converts the Map object into the interface (using a Proxy), it looks like you can just re-retrieve the original map by getting the InvocationHandler's delegate.
def i = 1
def m = [ hasNext:{ true }, next:{ i++ } ]
Iterator iter = m as Iterator
def d = java.lang.reflect.Proxy.getInvocationHandler(iter).delegate
assert d.is(m)
Note: This depends on the internals of the Groovy code so use at your own risk:
Interesting question... Short answer, no. Long answer, maybe... Assuming you have something like this:
def i = 1
Iterator iter = [ hasNext:{ true }, next:{ i++ } ] as Iterator
then calling
println iter.take( 3 ).collect()
prints [1,2,3]
Now, you can declare a method to do this:
def mapFromInterface( Object o, Class... clz ) {
// Get a Set of all methods across the array of classes clz
Set methods = clz*.methods.flatten()
// Then, for each of these
methods.collectEntries {
// create a map entry with the name of the method as the key
// and a closure which invokes the method as a value
[ (it.name): { Object... args ->
o.metaClass.pickMethod( it.name, it.parameterTypes ).invoke( o, args )
} ]
}
}
This then allows you to do:
def map = mapFromInterface( iter, Iterator )
And calling:
println map.next()
println map.next()
Will print 4 followed by 5
printing the map with println map gives:
[ remove:ConsoleScript43$_mapFromInterface_closure3_closure4#57752bea,
hasNext:ConsoleScript43$_mapFromInterface_closure3_closure4#4d963c81,
next:ConsoleScript43$_mapFromInterface_closure3_closure4#425e60f2 ]
However, as this is a map, any class which contains multiple methods with the same name and different arguments will fail. I am also not sure how wise it is to do this in the first case...
What is your use-case out of interest?