Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge two maps to resultant Map in Groovy

I have two maps as:

firstMap = [totalProjectCount:53, activeProjectCount:29, completedProjectCount:1, userCount:85]
secondMap = [totalProjectCount:48, activeProjectCount:41, completedProjectCount:0, userCount:123]

and i need result such as:

resultMap = [totalProjectCount:101, activeProjectCount:70, completedProjectCount:1, userCount:208]
like image 659
Amar Magar Avatar asked Dec 19 '22 21:12

Amar Magar


2 Answers

Yet another Groovy alternative:

def firstMap = [totalProjectCount:53, activeProjectCount:29, completedProjectCount:1, userCount:85, a:1]
def secondMap = [totalProjectCount:48, activeProjectCount:41, completedProjectCount:0, userCount:123, b:2]

def result = [firstMap, secondMap]*.keySet().flatten().toSet().collectEntries { k ->
    [k, (firstMap[k] ?: 0) + (secondMap[k] ?: 0)]
}

assert result == [a:1, b:2, completedProjectCount:1, userCount:208, activeProjectCount:70, totalProjectCount:101]

This also works if you have values in one map that don't exist in the other (see a and b)

like image 174
tim_yates Avatar answered Dec 31 '22 05:12

tim_yates


To make this a reusable part of your toolbelt, it's important, to extract the function you want to merge with.

The merging itself is then just the creation of a new map with all the keys of the maps and the values reduced with the passed function.

Map mergeWith(Closure fn, Map... maps) {
    maps*.keySet().sum().collectEntries{k->
        [k, maps.findAll{it.containsKey k}*.get(k).inject(fn)]
    }
}

def m1 = [a:53, b:29, c:1, x:85]
def m2 = [a:48, b:41, c:0, y:123]
def m3 = [z:42]

// sum
assert mergeWith({a,b->a+b}, m1, m2, m3)==[a:101, b:70, c:1, x:85, y:123, z:42]
// product
assert mergeWith({a,b->a*b}, m1, m2, m3)==[a:2544, b:1189, c:0, x:85, y:123, z:42]

edit: as requested in the comments, some explanations

mergeWith takes a reducing function (a function, that gets called with two params and returns one value) and some maps. This means, you can a) use it for other operations than just the sum and b) you can use it for more than one map.

maps*.keySet().sum() is the union of all keys. Then we collectEntries (build a new map) over that set of keys. For the value find all maps, that contains the key (this makes this work nicely even in cases, where the actual value is falsey) and fetch the values of k via the spread operator. Then we reduce (oddly named inject) the list of values with the passed function.

like image 40
cfrick Avatar answered Dec 31 '22 05:12

cfrick