Update map using findAll and each in groovy - groovy

I would like to update values in map in Groovy filling certain criteria. Here is my code:
def m = [:]
m['a'] = 1
m['b'] = 2
m['d'] = 3
m.findAll { it.value > 1}.each {
it.value = 4
}
println m
But the result is following:
[a:1, b:2, d:3]
Is there any way to do it using both findAll and each? Or I must use
m.each {if (it.value>1) it.value=4}

The root cause is findAll returns a new Map instance.
So you could try:
newMap = m.findAll { it.value > 1}.each {
it.value = 4
}
println m //No change
println newMap //This is what you need!
output is
[a:1, b:2, d:3]
[b:4, d:4]

In each case, the values you are iterating with the each are map entries with a pointer to key and value. When you set it.value you are not replacing what is in the map. You are only updating the pointer in the map entry. To actually set the value, you will need to do the following:
m.findAll { it.value > 1 }.each { m[it.key] = 4 }

Related

Groovy: Get index of all occurences of sublist from arraylist

I am new to groovy and trying to find the indexes of all sublists in a list.
I am trying to use something like Collections.indexOfSubList like in java but it gives exception saying it applies on Lists and not ArrayLists.
So I am trying to define my own function. I am finding all the indices of all the elements in the smaller list existing in the longer list and then subtracting the indices of the result array. If it comes to 1 then I am considering that index to a sublist.
I know that I have the logic a little twisted. Can somebody guide with a better and efficient way of doing this.
Below is my code:
List list1 = [1,2,3,4,5,6,1,2,3]
List list2 = [1,2]
index1 = list1.findIndexValues {
it == list2[0];
}
index2 = list1.findIndexValues {
it == list2[1];
}
println index1
println index2
result = []
for (int i = 0; i < index1.size(); i++) {
result.add(index2[i]-index1[i]);
}
println result
Edit: no longer uses Collections due to new issue re: Elastic Search.
The following code traverses along the source list, creating a sublist. It checks the sublist to see if it starts with the target list. See the asserts below (e.g. the indexes are 0-based):
def listStartsWithSubList = { source, target ->
def result = false
if (source.size() >= target.size()) {
result = true
target.eachWithIndex { item, index ->
result = result && (item == source[index])
}
}
result
}
def indexOfSubLists = { source, target ->
def results = []
source.eachWithIndex { item, index ->
def tmpList = source[index..source.size()-1]
if (listStartsWithSubList(tmpList, target)) {
results << index
}
}
results
}
assert [1] == indexOfSubLists([1,2,3], [2,3])
assert [2] == indexOfSubLists([1,2,3], [3])
assert [] == indexOfSubLists([1,2,3], [4])
assert [0,6] == indexOfSubLists([1,2,3,4,5,6,1,2,3], [1,2])

How to change variables with in a string into a Map

I have let's say 100 variables in a string , my requirement is to automatically create a Map out of the string:
String str = "$$test$$ $$test2$$ $$test$$ $$test3$$"
Expected Result:
["test":test, "test2":test2, "test3":test3];
EDIT (for dsharew)
This is the last version of my code
def list = queryText.findAll(/\$\$(.*?)\$\$/)
def map = [:]
list.each{
log.debug(it)
it = it.replace("\$\$", "")
log.debug(it)
map.putAt(it, it)
}
log.debug(list)
log.debug(map)
queryText = queryText.replaceAll(/\$\$(.*?)\$\$/) { k -> map[k[1]] ?: k[0] }
log.debug(queryText)
And the logs print the following result:
$$test$$
test
$$test2$$
test2
$$test$$
test
$$test3$$
test3
[$$test$$, $$test2$$, $$test$$, $$test3$$]
{test=test, test2=test2, test3=test3}
test test2 test test3
This should do what you want:
def queryText = "\$\$test\$\$ \$\$test2\$\$ \$\$test\$\$ \$\$test3\$\$"
toMap(queryText.findAll(/\$\$(.*?)\$\$/));
def toMap(list){
def map = [:]
list.each{
it = it.replace("\$\$", "")
map.putAt(it, it)
};
println map;
return map;
}
Following #dsharew answer, I've reduced it a little bit more:
​def queryText = "\$\$test\$\$ \$\$test2\$\$ \$\$test\$\$ \$\$test3\$\$"
def resultMap = queryText
.findAll(/\$\$(.*?)\$\$/)
.collectEntries { String next ->
[next.replace("\$\$", "")] * 2
}
collectEntries can be used to return a map from a collection if it returns a map or a tuple for every entry in the collection.
If you multiply a list by n, you are creating a bigger list with n times its content
BTW cool problem!
This is what I came up with
String str = '$$test$$ $$test2$$ $$test$$ $$test3$$'
str.replaceAll('\\$\\$', '').split(' ').collectEntries { [(it):it] }

How to merge two maps in groovy

Question:
How to merge the maps while summing up values of common keys among the maps.
Input:
[a: 10, b:2, c:3]
[b:3, c:2, d:5]
Output
[a:10, b:5, c:5, d:5]
Extended Question:
How to merge the original 2 maps, by applying a function (Closure) on the values of the common keys in the 2 maps. i.e.. instead of simply summing up the values of common keys let the user specify the function to use.
For eg: if user wants to use 'min' function instead of summing, then one can specify min to get [a:10, b:2, c:2, d:5] as the result.
You could use inject with ?: for when the map's value for the key is null:
map1 = [a:10, b:2, c:3]
map2 = [b:3, c:2, d:5]
(map1.keySet() + map2.keySet())
.inject([:]) {m, k -> m[k] = (map1[k] ?: 0) + (map2[k] ?: 0); m }
which evaluates to
[a:10, b:5, c:5, d:5]
Alternatively you can use collectEntries (the closure is not as ugly this way):
map1 = [a:10, b:2, c:3]
map2 = [b:3, c:2, d:5]
(map1.keySet() + map2.keySet())
.collectEntries {[(it) : (map1[it] ?: 0) + (map2[it] ?: 0)]}
To make this generic, allow passing in a closure. But collectEntries already allows that, you don't gain much.
Below groovy script uses and addresses the OP question using closure. That will help to decide user to choose the merge strategy for the value of each key in the merged map.
NOTE: The script sample is using 3 maps to make sure the script is able to handle the merging of multiple maps. This solution provided here would scale even if there are more maps to be handled.
While merging, it is possible that each map may not have all the keys, so it is possible to have null when user tries to get the value. Hence removing null from list that is passed to the Collection.
/**
* this script to merge the maps based on the closure provided by user based on different use case
*/
//For sample, taking below 3 maps
def map1 = [a:10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def map3 = [d:3,a:4,e:9]
//Below method takes list of maps and closure as input and returns merged map
def getMergedMap(list, closure) {
def keys = [] as Set
list.each { element -> keys.addAll(element.keySet()) }
def map = [:]
keys.each { k ->
def items = []
list.each { items.add(it[k]) }
map[k] = closure(items)
}
map
}
//Create the list of maps
def mapList = [map1, map2, map3]
//Call the above method and pass the closure are need for merging condition, here min of matched key values from multiple maps
def newmap = getMergedMap(mapList) { list -> Collections.min(list - null) }
println newmap
//Call the above method and pass the closure are need for merging condition, here max of matched key values from multiple maps
newmap = getMergedMap(mapList) { list -> Collections.max(list - null) }
println newmap
//Call the above method and pass the closure are need for merging condition, here sum of matched key values from multiple maps
newmap = getMergedMap(mapList) { list -> (list-null).sum() }
println newmap
Output for the above code:
[a:4, b:2, c:2, d:3, e:9]
[a:10, b:3, c:3, d:5, e:9]
[a:14, b:5, c:5, d:8, e:9]
UPDATE: If you want default behavior while merging, retains value from last map in the order of merging, below closure call can be used
newmap = getMergedMap(mapList) { list -> (list-null).last() }
println newmap
And results to:
[a:4, b:3, c:2, d:3, e:9]
You may quickly test the script from here Demo
UPDATE2:
The above getMeredMap is simple and readable. Of course, can be groovified / condensed using multiple inject's to as shown below on-liner:
def getNewMap(list, closure) {
list.inject([], { klist, map -> klist.addAll(map.keySet()); klist as Set }).inject([:]) { m, k -> m[k] = closure(list.inject([]){ vlist,map -> vlist << map[k] });m }
}
UPDATE 3
You may also simplify calling code by defining the closures separately for merged value strategy. That makes little simplify, imo. Also handled null values while merging inside instead of letting user handle outside and this would more clean to those who uses getMergedMap method.
//Merging of multiple maps with different merge strategies
//And handled null inside of mergeMethod instead of outside like earlier
def map1 = [a:10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def map3 = [d:3, a:4, e:9]
//Input map list and Merge strategy closure and handling null
def getMergedMap(list, closure) {
list.inject([],{ klist, map -> klist.addAll(map.keySet());klist as Set}).inject([:]) { m, k -> m[k] = closure(list.inject([]){ vlist,map -> vlist << map[k];vlist-null });m }
}
def mapList = [map1, map2, map3]
//Closures for merged value strategy
def minValue = { list -> Collections.min(list) }
def maxValue = { list -> Collections.max(list) }
def totalValue = { list -> list.sum() }
def defaultValue = { list -> list.last() }
//Call merge maps with strategies and assert
assert [a:4, b:2, c:2, d:3, e:9] == getMergedMap(mapList, minValue)
assert [a:10, b:3, c:3, d:5, e:9] == getMergedMap(mapList, maxValue)
assert [a:14, b:5, c:5, d:8, e:9] == getMergedMap(mapList, totalValue)
assert [a:4, b:3, c:2, d:3, e:9] == getMergedMap(mapList, defaultValue)
Would this suffice?
Map one = [a:10, b:2, c:3]
Map two = [b:3, c:2, d:5]
Map mergeOn(Map one, Map two, Closure closure) {
two.inject([:] << one) { acc, key, val ->
key in acc.keySet() ? acc[key] = closure(acc[key], val) : acc << [(key): val]
acc
}
}
assert mergeOn(one, two) { a, b -> a + b } == [a:10, b:5, c:5, d:5]
assert mergeOn(one, two) { a, b -> a - b } == [a:10, b:-1, c:1, d:5]
assert mergeOn(one, two) { a, b -> a * b } == [a:10, b:6, c:6, d:5]
assert mergeOn(one, two) { a, b -> Math.max(a, b) } == [a:10, b:3, c:3, d:5]
assert mergeOn(one, two) { a, b -> Math.min(a, b) } == [a:10, b:2, c:2, d:5]
Here is a simple solution that collects the unique keys, the values for each key as an array, and applies the lambda to the array of values for each key. In this first one the lambda takes an array:
def process(def myMaps, Closure myLambda) {
return myMaps.sum { it.keySet() }.collectEntries { key ->
[key, myLambda(myMaps.findResults { it[key] })]
}
}
def map1 = [a: 10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def maps = [map1, map2]
def sumResult = process(maps) { x -> x.sum() }
def prodResult = process(maps) { x -> x.inject(1) { a, b -> a * b } }
def minResult = process(maps) { x -> x.inject(x[0]) { a, b -> a < b ? a : b } }
assert sumResult == [a:10, b:5, c:5, d:5]
assert prodResult == [a:10, b:6, c:6, d:5]
assert minResult == [a:10, b:2, c:2, d:5]
In this 2nd version the lambda expression takes two values:
def process(def myMaps, Closure myLambda) {
return myMaps.sum { it.keySet() }.collectEntries { key ->
[key, { x ->
x.subList(1, x.size()).inject(x[0], myLambda)
}(myMaps.findResults { it[key] })]
}
}
def map1 = [a: 10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def maps = [map1, map2]
def sumResult = process(maps) { a, b -> a + b }
def prodResult = process(maps) { a, b -> a * b }
def minResult = process(maps) { a, b -> a < b ? a : b }
assert sumResult == [a:10, b:5, c:5, d:5]
assert prodResult == [a:10, b:6, c:6, d:5]
assert minResult == [a:10, b:2, c:2, d:5]
The first one can be accomplished by:
/* Transform entries in map z by adding values of keys also present in zz
* Take any entries in map zz whose keys are not in z. Add the result.
*/
Map mergeMaps(Map z, Map zz){
Map y = z.inject([:]) { result, e -> zz.keySet().contains(e.key) ? result << [(e.key) : e.value + zz[e.key]] : result << e }
Map yy = zz.findAll { e -> !z.keySet().contains(e.key) }
y + yy
}
Let's use this now at the Groovy console:
mergeMaps([a: 10, b:2, c:3], [b:3, c:2, d:5])
Result: [a:10, b:5, c:5, d:5]
The extended question (more generic) one can be accomplished by a small tweak:
Map mergeMapsWith(Map z, Map zz, Closure cls){
Map y = z.inject([:]) { result, e -> zz.keySet().contains(e.key) ? result << [(e.key) : cls.call(e.value,zz[e.key])] : result << e }
Map yy = zz.findAll { e -> !z.keySet().contains(e.key) }
y + yy
}
Let's use this now at the Groovy console:
mergeMapsWith([a: 10, b:2, c:3], [b:3, c:2, d:5]) { a, b -> Math.min(a,b)}
Result: [a:10, b:2, c:2, d:5]
or if we wanted to merge with a multiplication:
mergeMapsWith([a: 10, b:2, c:3], [b:3, c:2, d:5]) { a, b -> a * b }
Result: [a:10, b:6, c:6, d:5]

Nesting groovy each{} inside find{}

I know we cannot return from .each{} closure in groovy like .find{} closure. Still I am curious why the below code execute only first iteration of the .find{}.
def findlist = [1,2,3,4,5]
def eachlist = [7,6,5]
findlist.find
{
int findelem = it
println "findelem : " + findelem
eachlist.each
{
int eachelem = it
println "eachelem : " + eachelem
if(it == findelem)
{
return true
}
return false
}
}
It prints:
findelem : 1
eachelem : 7
eachelem : 6
eachelem : 5
Why find{} exits after first iteration?
PS: I understand this code might not have any practical significance, just curious about groovy behavior.
Because each returns unmodified collection is iterating on. The returned collection evaluates to true, hence find stops after first iteration.
Have a look at the code below:
assert [1, 2].each { println it } == [1,2]
assert [1,2].find { println it; [3, 4].each { e -> println e } }
You need to nest find instead of each.
Just to complete Opal's answer (each returns the collection so it evaluates to true if it is not empty), you can use a local variable inside the find closure to return the found value. Simplifying your code a bit:
assert 5 == findlist.find { findelem ->
println "findelem : " + findelem
boolean found
eachlist.each { eachelem ->
println "eachelem : " + eachelem
found = (eachelem == findelem)
}
found
}
However, there is a nicer and groovier way:
assert 5 == findlist.find { it in eachlist }

Difference of two maps in groovy using collectEntries

I am trying to find the difference between values in two maps
#Test
void testCollecEntries() {
def mapOne= ["A":900,"B":2000,"C":1500]
def maptwo = ["A":1000,"D":1500,"B":1500]
def balanceMap = maptwo.collectEntries { key, value-> [key:value-mapOne[key]] }
println balanceMap
}
I am trying to find the difference of values from maptwo with that of the values from mapOne. If the entry doesn't exist i need to ignore. This gives me a null pointer exception.
Appreciate any help.
It will throw NPE because you are looking for key "D" in mapOne which is not available.
You can avoid that by a null safe operation and default value to 0.
def one= [A:900, B:2000, C:1500]
def two = [A:1000, D:1500, B:1500]
def result = two.collectEntries{k,v -> [k, (v - (one[k]?:0))]}
println result
//Print
[A:100, D:1500, B:-500]
In case, you want to consider the common keys then use:
def result = two.collectEntries{k,v -> one[k] ? [k, (v - one[k])] : [:]}
//or
//def result = two.collectEntries{k,v -> (k in one.keySet()) ? [k, (v - one[k])] : [:]}
//Print
[A:100, B:-500]
You could look at this good example: http://groovyconsole.appspot.com/script/364002

Resources