loop find pattern in map - groovy - 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"
}

Related

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.

How to dynamic call class instance attribute?

I am doing some parse work with hl7apy parse, and i occurred with one problem.
I use hl7apy to parse hl7 message, which can be parse:
from hl7apy.parser import parse_message
message = "MSH|^~\&|HIS|HIS|MediII|MediII|20170902141711||ORM^O01^ORM_O01|15b 37e7132504a0b95ade4654b596dc5|P|2.4\r"
msg = parse_message(message, find_groups=False)
print(msg.msh.msh_3.msh_3_1.value)
output:
'HIS'
so, how can i get field value dynamically according to field config?
for example, the msh field config :
{
"field": "msh",
"field_index": [3,1]
}
so the value can be find with:
msg.msh.msh_3.msh_3_1.value
and if config change to:
{
"field": "pid",
"field_index": [2,4]
}
the get field line will be:
msg.pid.pid_2.pid_2_4.value
You could combine a few list comprehensions and use getattr recursively.
# recursively get the methods from a list of names
def get_method(method, names):
if names:
return get_method(getattr(method, names[0]), names[1:])
return method
field_config = {
'field': 'msh',
'field_index': [3, 1]
}
# just get the field
field = field_config['field']
# get a list of the indexes as string. ['3', '1']
indexes = [str(i) for i in field_config['field_index']]
# join the indexes with a '_' starting with nothing
# and put it in a list of names. ['msh', 'msh_3', 'msh_3_1']
names = ['_'.join([field] + indexes[:i]) for i in range(len(indexes) + 1)]
# get the method from the recursive function
method = get_method(msg, names)
print(method.value)
As a disclaimer, I had no way of testing it so it may not work exactly as you expect it. But this should be a good starting point.

Filter the output in the brackets as a list

I have a lines like :
[Something-26543] One ticket
[Something-23121] Second ticket
[Something-21243] Third ticket and so on
Can someone advice if there is a a way to grep only the line between the square brackets.
Before:
[Something-26543] Another Ticket
After filtering:
Something-26543
Something-23121
Something-21243
What I wrote as an example. Note "asdfasdf" is not between any brackets, so it should not be matched:
def changeString = ['[DPDHLPA-26607] Updating robots.txt', '[DPDHLPA-2321] [DPDHLPA-2322] Updating something.txt', 'asdfasdf']
def TICKET_LIST = (changeString =~ /(?<=\[)[^]]+(?=\]/[0].readLines().unique().sort().join('\n')
print TICKET_LIST //to see if it got the filtered list
But then TICKET LIST shows only the first filter. I need to collect all outputs, because later I will do for loop on "TICKET_LIST" and assemble an URL. I've tried also with collectMany, but that didn't really work for me.
You should be able to do:
def ticketList = changeString.findResults { str ->
(str =~ /\[([^]]+)].*/).with {
it.matches() ? it[0][1] : null
}
}

Getting value from nested map in groovy using a nested key

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

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