Dave CK Dave CK - 1 year ago 270
Groovy Question

Remove null values and empty maps from Map of Maps (to undefined depth) in Groovy

I need to iterate through a Map of Maps (which may themselves contain Maps to an unknown depth) and remove any null values. In the case of all values being null (leaving an empty map) then then entire map should be removed.

To clarify, looking at myMap below I wish to remove b, e, and j because they are null, but also remove i as when j is removed it contains an empty map, and k which is an empty map to start.

I can use findAll to remove the top level nulls (as shown in the first assert) but wondered if there was a Groovy way to resolve the second assert.

Map myMap = [ a : 1
, b : null
, c : [ d : 1
, e : null
, f : [ g : 1
, h : 2 ]
, i : [ j : null ]
, k : [:] ] ]

assert [a:1, c:[d:1, e:null, f:[g:1, h:2], i:[j:null], k:[:]]] == myMap.findAll(){ it.value != null }

assert [a:1, c:[d:1, f:[g:1, h:2]]] == '<<SOME CODE>>'

Any ideas greatly appreciated.

Answer Source

There may be simpler ways, but you can use collectEntries to recurse through the map values, and then use findAll to remove the empty ones, ie:

def deepPrune = { Map map ->
    map.collectEntries { k, v -> [k, v instanceof Map ? owner.call(v) : v]}
       .findAll { k, v -> v }

assert deepPrune(myMap) == ['a':1, 'c':['d':1, 'f':['g':1, 'h':2]]]

Or as a method:

def deepPrune(Map map) {
    map.collectEntries { k, v -> [k, v instanceof Map ? deepPrune(v) : v]}
       .findAll { k, v -> v }
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download