Flattening a nested map in Groovy

Let's say I have the following structure:


Map<String,Map<String,Integer>> nestedMap = [
  "x": [
    "a": 2,
    "b": 3,
    "c": 4
  ],
  "y": [
    "a": 20,
    "b": 30,
    "c": 40
  ],
  "z": [
    "a": 200,
    "b": 300,
    "c": 400
  ]
]

I want to flatten this structure and get:

[
  "x-a" : 2, 
  "x-b" : 3, 
  "x-c" : 4,
  "y-a" : 20, 
  "y-b" : 30, 
  "y-c" : 40,
  "z-a" : 200, 
  "z-b" : 300, 
  "z-c" : 400
]

How can I do this with collect/collectEnteries etc?

Alternatively, as you can see the values are quite similar. so the way I have constructed this nested map structure, was by running a for loop iterating through [x,y,z] and setting the entries of my map to be equal to a function that iterates through [a,b,c] and returns a map. Is there a way of skipping this nested structure and functionally append the elements in a big map?


You can create a recursive function that will loop over the map and flatten it.

Map<String,Map<String,Integer>> nestedMap = [
"x": ["a": 2, "b": 3, "z": 4],
"y": ["a": 20, "b": 30, "c": 40],
"z": ["a": 200, "b": 300, "c": 400]
]

String concatenate(String k, String v) {
"${k}-${v}"
}

def flattenMap(Map map) {    
    map.collectEntries { k, v -> 
       v instanceof Map ? 
          flattenMap(v).collectEntries {  k1, v1 -> 
             key = concatenate(k,k1)
            [ (key): v1 ]  
         } :
            [ (k): v ]  
     } 
}


print flattenMap(nestedMap)​

Regarding the alternate question (and echoing a comment by @damahapatro), consider the following.

Given two lists:

def list1 = ['x', 'y', 'z']
def list2 = ['a', 'b', 'c']

and a function f that gives the appropriate values, then we can use inject (which will accumulate a map while iterating over the combos (e.g. [x,a]):

def m = [list1, list2].combinations().inject([:]) { map, combo ->
    assert combo.size() == 2
    def a = combo[0]
    def b = combo[1]
    map["${a}-${b}" as String] = f(a,b)
    map
}

assert m == [
"x-a" : 2, "x-b" : 3, "x-c" : 4,
"y-a" : 20, "y-b" : 30, "y-c" : 40,
"z-a" : 200, "z-b" : 300, "z-c" : 400
]