Getting value from nested map in groovy using a nested key - groovy

Suppose I have nested map like this
def someMap = [
a : [
b : [
c : "value",
d : "anothervalue"
]
]
]
I get the key at runtime as a string. Say it is "a.b.c"
How can I pull the value out of the map?
I know I can do myMap.a.b.c but for me, "a.b.c" is single string, I found out about at runtime?
Thanks

You could just go down the nodes likes this and try to get the respective child:
def key = "a.b.c"
def entry = someMap
key.split('\\.').each { entry = entry?.get(it) }
println entry?.value

Related

Groovy - mapping with groupBy and counting instances of an element within the result

I am starting with a collection as such:
def finalDocument = [[problem:problem1,fixed:true], [problem:problem1,fixed:false],[problem:problem1,fixed:false],[problem:problem2,fixed:true],[problem:problem2,fixed:false]]
What I want to do is create a map containing the problem and counts of how many are fixed, like this:
["problem1":[true:1,false:2],"problem2":[true:1,false:1]]
My end goal is to produce a percentage of fixed vs total for each problem
I'm trying to use groupBy to achieve this:
def output = finalDocument.groupby({it.problem},{it.fixed})
The resulting map is nested correctly but it gives me a list of the documents rather than a count of each, which I'm not sure how to achieve (I'm new to Groovy and scripting in general).
I'd put it like so:
def finalDocument = [[problem:'problem1',fixed:true], [problem:'problem1',fixed:false],[problem:'problem1',fixed:false],[problem:'problem2',fixed:true],[problem:'problem2',fixed:false]]
def res = finalDocument.groupBy{ [ it.problem, it.fixed ] }.inject( [:].withDefault{ [:] } ){ acc, grp, vals -> acc[ grp.first() ][ grp.last() ] = vals.size(); acc }
assert res.toString() == '[problem1:[true:1, false:2], problem2:[true:1, false:1]]'

How can I get next value from a python dictionary after matching the key value with some string

I am trying to get the value (fcs) when the key value is "Team". I tried with python code but was not able to get the value once the key is matched with "Team".
[{'key':'appid','value':'xyz'},{'key':'Team','value':'fcs'},{'key':'incident','value':'a1435621'}]
Another solution, using next():
lst = [
{"key": "appid", "value": "xyz"},
{"key": "Team", "value": "fcs"},
{"key": "incident", "value": "a1435621"},
]
team = next(d["value"] for d in lst if d["key"] == "Team")
print(team)
Prints:
fcs
Python dictionaries have the structure
dict = {
key1: value1,
key2: value2,
}
I would recommend changing your data structure into the form above rather than have an list of dictionaries. That way, your data will look like this
data = {
appleid:"xyz",
Team:"fcs",
incident:"a1435621"
}
That way, you can call your data like this
print(data.Team) #this will output "fcs"
If you still want to keep your original structure of data, you write a for loop that goes through each dictionary and once you find the one that matches, you can store the index and call it
index = 0
for i in range(0, len(data)):
if data[i].key == "Team":
index = i
print(data[i].value)

How to use eval in nested dictionary in Python

I have a dictionary similar to the below structure
sample_dict = {
'key1' : 'getVal1' ,
'key2' : 'getVal2' ,
'level1' :
{
'key3' : 'getVal3' ,
'level2' :
{
'key4' : 'getVal4'
}
}
}
I want to evaluate entire dictionary without accessing individual inner dict separately. (Eg : Without accessing 'level1' and 'level2' keys.)
The values are separate functions.. example is mentioned below.
def getVal1():
return "Some Calculation"
If it was a simple dict without nesting, it would be very simple to process, something like below.
final_dict = {k : eval(v) for k,v in sample_dict .items()}
But with nested dictionary, I am not able to construct.
If there is any generic way for this, it will be really helpful.

loop find pattern in map - groovy

I want to loop through a map so if regex condition/pattern is met I will put the value into another map, else I will put in the map {trigger-all=true}.
This is what I got so far :
def patternsFunc = [
/(?s).*vee\/.*/ : "vee",
/(?s).*$HelmServicesPath\/vee\/.*/ : "vee",
/(?s).*rest\/.*/ : "rest",
/(?s).*$HelmServicesPath\/rest\/.*/ : "rest",
/(?s).*service\/.*/ : "service",
/(?s).*$HelmServicesPath\/service\/.*/ : "service",
]
patternsFunc.find { pattern, cname ->
if (file.find(pattern)) {
triggermap."trigger-${cname}" = true
assert triggermap."trigger-${cname}"
return true //found
} else {
return false
}
}
But there are 2 problems with this code:
the loop here going through every item on the patternsFunc map, so everything I put inside the "else" will happen whenever any of the conditions aren't met for every Item and not on the entire maps I need.
How do I put all values from the array to the map inside the "else"?
What I really need to achieve is in case no conditions are met, This will be the content of the map: {trigger-all=true}.
And when a condition(let's say "vee" and "rest") is met, This will be the content of the map: {trigger-rest=true,trigger-vee=true}.
First of all, find
finds the first result, but you want to find all, so use findAll.
Next don't mix side-effects into find or alike. Do the "finding"
first, then deal with the results. In this case, you want to build a
map from all the results. Use collectEntries for that.
And finally
you want a fallback, if nothing is found. So you can use the elvis
operator ?: to provide an alternative, if the resulting map is empty.
E.g.
def patterns = [
(/(?s).*service\/.*/): "service",
]
def file = '/service/'
def triggerMap = patterns.findAll{ // find relevant map entries
file.find(it.key)
}.collectEntries{ // build your map
["trigger-${it.value}", true]
} ?: ["trigger-all": true] // provide a fallback
Alternatively to what cfrick suggested, you can use inject method with an empty map as the initial value. That way you can iterate over all patterns, add entries if the pattern is found, and in the end, you can return ["trigger-all": true] map if no pattern was found.
Below you can find an example that tests 3 different file paths.
def patternsFunc = [
/(?s).*vee\/.*/ : "vee",
/(?s).*rest\/.*/ : "rest",
/(?s).*service\/.*/ : "service"
]
def files = [
"/tmp",
"/tmp/vee/1",
"/tmp/vee/rest/1"
]
files.each { file ->
def result = patternsFunc.inject([:]) { map, pattern, cname ->
file.find(pattern) ? map + ["trigger-$cname": true] : map
} ?: ["trigger-all": true]
println "Result for $file: $result"
}

How to loop through a map and display only one row if there is matching values

I have a map with differnt keys and multiple values.If there is any matching job among different keys,I have to display only one row and grouping code values.
def data = ['Test1':[[name:'John',dob:'02/20/1970',job:'Testing',code:51],[name:'X',dob:'03/21/1974',job:'QA',code:52]],
'Test2':[name:'Michael',dob:'04/01/1973',job:'Testing',code:52]]
for (Map.Entry<String, List<String>> entry : data.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
values.eachWithIndex{itr,index->
println("key is:"+key);
println("itr values are:"+itr);
}
}
Expected Result : [job:Testing,code:[51,52]]
First flatten all the relevant maps, so whe just have a flat list of all of them, then basically the same as the others suggested: group by the job and just keep the codes (via the spread operator)
def data = ['Test1':[[name:'John',dob:'02/20/1970',job:'Testing',code:51],[name:'X',dob:'03/21/1974',job:'QA',code:52]], 'Test2':[name:'Michael',dob:'04/01/1973',job:'Testing',code:52]]
assert data.values().flatten().groupBy{it.job}.collectEntries{ [it.key, it.value*.code] } == [Testing: [51, 52], QA: [52]]
Note: the question will be changed according to the comments from the other answers.
Above code will give you the jobs and and their codes.
As of now, it's not clear, what the new expected output should be.
You can use the groovy's collection methods.
First you need to extract the lists, since you dont need the key of the top level element
def jobs = data.values()
Then you can use the groupBy method to group by the key "job"
def groupedJobs = jobs.groupBy { it.job }
The above code will produce the following result with your example
[Testing:[[name:John, dob:02/20/1970, job:Testing, code:51], [name:Michael, dob:04/01/1973, job:Testing, code:52]]]
Now you can get only the codes as values and do appropriate changes to make key as job by the following collect function
def result = groupedJobs.collect {key, value ->
[job: key, code: value.code]
}
The following code (which uses your sample data set):
def data = ['Test1':[name:'John', dob:'02/20/1970', job:'Testing', code:51],
'Test2':[name:'Michael', dob:'04/01/1973', job:'Testing', code:52]]
def matchFor = 'Testing'
def result = [job: matchFor, code: data.findResults { _, m ->
m.job == matchFor ? m.code : null
}]
println result
results in:
~> groovy solution.groovy
[job:Testing, code:[51, 52]]
~>
when run. It uses the groovy Map.findResults method to collect the codes from the matching jobs.

Resources