Groovy: Confusion about Map Reassign via `getOrDefault` - groovy

I'm going to initialize a empty Map dynamically in a loop, i.e. key1 and key2 are variables in the loop:
Map<String, Map<String, List>> map = [
key1: [
key2: []
]
]
I'm trying initialize the map structure by getOrDefault in two ways. One (y) using the hardcode as key name, another (x) using the variable as the keyname:
Map y = [:]
Map x = [:]
String b = 'b'
String c = 'c'
y = y.getOrDefault( "b" , [ "b" : [:] ] )
.getOrDefault( "c" , [ "b" : [ "c" : []] ] )
x = x.getOrDefault( "${b}" , [ "${b}" : [:] ] )
.getOrDefault( "${c}" , [ "${b}" : [ "${c}" : []] ] )
However, when I try get result of map.b.c,
Map x:
x.get("${b}").get("${c}") : works
x["${b}"]["${c}"] : java.lang.NullPointerException
Map y works well in all various ways
println """
y['b']['c'] : ${y['b']['c']}
y.b.c : ${y.b.c}
y.get('b').get('c') : ${y.get('b').get('c')}
x.get("\${b}") : ${x.get("${b}")}
x["\${b}"] : ${x["${b}"]} // *why null*
x.get("\${b}").get("\${c}") : ${x.get("${b}").get("${c}")}
"""
println """ x["\${b}"]["\${c}"] : ${x["${b}"]["${c}"]} """
==> result
y['b']['c'] : []
y.b.c : []
y.get('b').get('c') : []
x.get("${b}") : [c:[]]
x["${b}"] : null // *why null*
x.get("${b}").get("${c}") : []
Exception thrown
java.lang.NullPointerException: Cannot get property 'c' on null object
at ConsoleScript77.run(ConsoleScript77:21)
I want to know why x.get("${b}").get("${c}") works, but x["${b}"]["${c}"] got null
btw, here the dynamic map initialization details:
Map m = [
'x' : [
'name': 'x',
'size': ['1', '2'],
'age': '1',
],
'y': [
'name': 'x',
'size': ['2', '3'],
'age': '2'
]
]
Map a = [:]
m.each{ k, v ->
v.size.each {
String t = "${v.name}-${it}"
a = a.getOrDefault(t, ["${t}": [:]])
println """
t: ${t}
k: ${k}
a: ${a}
a.t: ${a.t}
a.get("${t}"): ${a.get("${t}")}
a["${t}"]: ${a["${t}"]}
"""
}
}
=== output:
t: x-1
k: x
a: [x-1:[:]]
a.t: null
a.get("x-1"): [:]
a["x-1"]: null
t: x-2
k: x
a: [x-2:[:]]
a.t: null
a.get("x-2"): [:]
a["x-2"]: null
....

Appreciate #daggett.
So the truth is:
y.each { k, v ->
println """
key: ${k} \t\t\t key.getClass(): ${k.getClass()}
value: ${v} \t\t value.getClass(): ${v.getClass()}
"""
}
x.each { k, v ->
println """
key: ${k} \t\t\t key.getClass(): ${k.getClass()}
value: ${v} \t\t value.getClass(): ${v.getClass()}
"""
}
==> result
key: b key.getClass(): class java.lang.String
value: [c:[]] value.getClass(): class java.util.LinkedHashMap
key: b key.getClass(): class org.codehaus.groovy.runtime.GStringImpl
value: [c:[]] value.getClass(): class java.util.LinkedHashMap
So, according to my previous ${x["${b}"]}, the key of x ( "${b}" ) was type conversion to String, and x only has the key belongs to GStringImpl, so the result is null
However, .get("${b}") works, because of :
println "${b}".getClass() // class org.codehaus.groovy.runtime.GStringImpl
Here more details:
Map x = [:]
String str = 'a'
x = x.getOrDefault( "${str}", [ "${str}" : [:] ] )
x.each { k, v -> println "${k}: ${k.getClass()}" }
Map x = [:]
String str = 'a'
x = x.getOrDefault( "${str}", [ "${str}" : [:] ] )
x.each { k, v -> println "${k}: ${k.getClass()}" } // a: class org.codehaus.groovy.runtime.GStringImpl
assert x == [ "${s}" : [:] ]
assert x.a == null
assert x."${str}" == null
assert x["${str}"] == null
assert x[str] == null
assert x.get(str) == null
assert x.get("${str}") == [:]

Related

Python3 - recursively replace all keys with periods in a nested dictionary

I am trying to clean up a nested dictionary before inserting it into Mongo. Some of the keys in the dict have periods in them so I need to replace them with underscores. Based on other posts I have seen I have come up with this (not working) code sample:
def get_recursively(search_dict):
new_dict = {}
for key, value in search_dict.items():
if '.' in key or ' ' in key:
new_dict[key.replace('.', '_').replace(' ', '_').lower()] = value
elif isinstance(value, dict):
results = get_recursively(value)
for key2, value2 in results.items():
new_dict[key] = dict(key2, value2)
elif isinstance(value, list):
for item in value:
if isinstance(item, dict):
more_results = get_recursively(item)
for key3, value3 in more_results.items():
new_dict[key] = dict(key3, value3)
else:
new_dict[key] = value
return new_dict
I am trying to make a new dictionary because when I tried to modify the existing dictionary I got an error about the dictionary changing during execution.
The code that is not valid (at least) is:
dict(key2, value2)
That is not valid syntax but hopefully shows my thought process at least.
Any help much appreciated.
If I understood right, is this want you meant?
def change_chars(string, chars, new_char):
new_string = string
for char in chars:
new_string = new_string.replace(char, new_char)
return new_string
def recursively_change_keys(obj, chars, new_char):
if isinstance(obj, list):
return [
recursively_change_keys(o, chars, new_char)
for o in obj
]
elif isinstance(obj, dict):
return {
change_chars(key, chars, new_char): recursively_change_keys(value, chars, new_char)
for key, value in obj.items()
}
return obj
So you just have to call it like recursively_change(search_dict, [ ".", " " ], "_")
Try:
import json
d = {
"some.key": [
{
"key.1": {"a": 1},
"key.2": 2,
"key.3": {"key.4": [3, 4, 5], "key.5": 6},
}
]
}
def transform(d):
if isinstance(d, dict):
return {k.replace(".", "_"): transform(v) for k, v in d.items()}
elif isinstance(d, list):
return [transform(v) for v in d]
else:
return d
# pretty print the dictionary:
print(json.dumps(transform(d), indent=4))
Prints:
{
"some_key": [
{
"key_1": {
"a": 1
},
"key_2": 2,
"key_3": {
"key_4": [
3,
4,
5
],
"key_5": 6
}
}
]
}

python recursion avoid result as global variable

res_dict = {}
def get_value(co , passed_dict ):
for k, v in passed_dict.items():
if isinstance(v, dict):
get_value(co, v)
else:
res_dict[k] = v
print("from else",res_dict)
return res_dict
def easy():
inner_dict = {
"test1" : {"test1_in" : "abc"},
"test2" : {"test1_in" : "xyz"}
}
dict1 = {}
count = 0
val_from_function= {}
key_list = ['key1','key2']
for key in key_list:
count = count + 1
val_from_function = get_value(count ,inner_dict)
print("before assign" ,dict1 )
dict1[key] = val_from_function
print("after assign" , dict1)
# dict1['key1'] = {'test1' : "abc"}
# dict1['key2'] = {'test1' : "xyz"}
print(dict1)
easy()
receiving output : {'key1': {'test1_in': 'xyz'}, 'key2': {'test1_in': 'xyz'}}
expected o/p : {'key1': {'test1_in': 'abc'}, 'key2': {'test1_in': 'xyz'}}
I understand the value of dict1 is updated with the last value as res_dict declared as global
variable.
I can solve it by appending the inner key value with outer key and then storing in dictionary.
I might solve it using ordered dictionary.
taking keys from list as outer key value (key1, key2 ..,key3000) is unknown.
Looking for suggestions on how to make this code better with expected o/p.
Have 3k key-pair values, same as sample pattern with more nested k,v & storing o/p as cache, so performance is not a very big issue here.
# This modifies passed_dict
def get_value(passed_dict, recur_dict_new={}, isInitial=True):
for k, v in passed_dict.items():
if isInitial:
recur_dict_new = {}
if isinstance(v, dict):
temp = get_value(v, recur_dict_new, isInitial=False)
passed_dict[k]= temp
else:
recur_dict_new[k]=v
return recur_dict_new
def easy():
inner_dict = {
"test1" : {"test1_in" : "abc"},
"test2" : {"test1_in" : "xyz"}
}
key_list = ['key1','key2']
for key in key_list:
get_value(inner_dict)
# dict1['key1'] = {'test1' : "abc"}
# dict1['key2'] = {'test1' : "xyz"}
print(inner_dict)
easy()
Thanks for looking, I have solved with one of the ways as mentioned above,
tested with 15k records with 3 more levels of nested JSON, the performance was okay(4ms)
O/p : {'test1': {'test1_in': 'abc'}, 'test2': {'test1_in': 'xyz'}}

Python: How to convert json comma separated key to a dictionary

I have a JSON in below format:
{
'166, 175': 't2',
'479': 't3'
}
I want to convert this to a map:
166: 't2'
175: 't2'
479: 't3'
src = {
'166, 175': 't2',
'479': 't3'
}
res = {}
for k, v in src.items():
for i in k.split(', '):
res[int(i)] = v
print(res)
You can use some dictionary comprehension here:
{
int(k): v
for ks, v in data.items()
for k in ks.split(',')
}
For the sample data, this gives us:
>>> {
... int(k): v
... for ks, v in data.items()
... for k in ks.split(',')
... }
{166: 't2', 175: 't2', 479: 't3'}
Bit complicated though
src = {
'166, 175': 't2',
'479': 't3'
}
output = dict(reduce(lambda a, b: a + b, map(lambda b:zip(b.split(', '), [a[b]] * len(b.split(', '))), src)))

How do I implement a comparator for a map in Groovy?

I have a map in Groovy:
['keyOfInterest' : 1, 'otherKey': 2]
There is a list containing a number of these maps. I want to know if a map exists in the list with keyOfInterest of a certain value.
If the data types were simple objects, I could use indexOf(), but I don't know how to do this with a more complicated type. E.g. (taken from the docs)
assert ['a', 'b', 'c', 'd', 'c'].indexOf('z') == -1 // 'z' is not in the list
I'd like to do something like:
def mapA = ['keyOfInterest' : 1, 'otherKey': 2]
def mapB = ['keyOfInterest' : 3, 'otherKey': 2]
def searchMap = ['keyOfInterest' : 1, 'otherKey': 5]
def list = [mapA, mapB]
assert list.indexOf(searchMap) == 0 // keyOfInterest == 1 for both mapA and searchMap
Is there a way to do this with more complicated objects, such as a map, easily?
While #dmahapatro is correct, and you can use find() to find the map in the list of maps that has the matching index... that's not what you asked for. So I'll show how you can get either the index of that entry in the list, or just whether a map with matching keyOfInterest exists.
def mapA = ['keyOfInterest' : 1, 'otherKey': 2]
def mapB = ['keyOfInterest' : 3, 'otherKey': 2]
def searchMap = ['keyOfInterest':1, 'otherKey': 55 ]
def list = [mapA, mapB]
// findIndexOf() returns the first index of the map that matches in the list, or -1 if none match
assert list.findIndexOf { it.keyOfInterest == searchMap.keyOfInterest } == 0
assert list.findIndexOf { it.keyOfInterest == 33 } == -1
// any() returns a boolean OR of all the closure results for each entry in the list.
assert list.any { it.keyOfInterest == searchMap.keyOfInterest } == true
assert list.any { it.keyOfInterest == 33 } == false
Note that there is no performance penalty for using one over the other as they all stop as soon as one match is found. find() gives you the most information, but if you're actually looking for the index or a boolean result, these others can also be used.
Simplest implementation would be to use find(). It returns null when criteria is not met in the supplied closure.
def mapA = ['keyOfInterest' : 1, 'otherKey': 2]
def mapB = ['keyOfInterest' : 3, 'otherKey': 2]
def list = [mapA, mapB]
assert list.find { it.keyOfInterest == 1 } == ['keyOfInterest':1, 'otherKey':2]
assert !list.find { it.keyOfInterest == 7 }

Sort values of a Map in Groovy

I have a map where a key holds multiple values
datamap = [ 'Antenna Software':[ 'Salarpuria', 'Cessna', 'Vrindavan Tech', 'Alpha Center' ],
'Ellucian':[ 'Malvern', 'Ellucian House', 'Residency Road'] ]
here i need to alphabetically sort the values
datamap = [ 'Antenna Software':[ 'Alpha Center', 'Cessna', 'Salarpuria', 'Vrindavan Tech' ],
'Ellucian':[ 'Ellucian House', 'Malvern', 'Residency Road' ] ]
how to do it in groovy way?
You should be able to do:
def sortedMap = datamap.sort().collectEntries { k, v ->
[ k, v.sort( false ) ]
}
If you're not bothered about sorting the keys of the map, you can get rid of the initial sort():
def sortedMap = datamap.collectEntries { k, v ->
[ k, v.sort( false ) ]
}
Explanation of sort( false ):
By default, the sort method in Groovy changes the original list, so:
// Given a List
def a = [ 3, 1, 2 ]
// We can sort it
def b = a.sort()
// And the result is sorted
assert b == [ 1, 2, 3 ]
// BUT the original list has changed too!
assert a != [ 3, 1, 2 ] && a == [ 1, 2, 3 ]
So if you pass false to sort, it leaves the original list alone, and just returns the sorted list:
// Given a List
def a = [ 3, 1, 2 ]
// We can sort it (passing false)
def b = a.sort( false )
// And the result is sorted
assert b == [ 1, 2, 3 ]
// AND the original list has remained the same
assert a == [ 3, 1, 2 ]

Resources