Default values on nested maps in Groovy - groovy

I have a JSON configuration file that I want to read with groovy
this file has "defaults" in case one key would be missing because no specific configuration has been added.
json
{
"presets": {
"default": "preset_default",
"Team1":{
"AppName1":{
"Component1": "preset_team1App1Comp1",
"default" : "preset_team1App1Default"
},
"AppName2":{
"Component1": "preset_team1App2Comp1",
"Component2": "preset_team1App2Comp2"
},
"default" : "preset_team1Default"
},
"Team2":{
"AppName1":{
"Component1": "preset_team2App1Comp1"
}
}
}
}
What would be the most groovy way to search for the value and falling back to the outer most "default" key?
here's how I implemented it but I'm not sure this would work:
if (presets[appTeam])
if (presets[appTeam][appName])
if (presets[appTeam][appName][compName])
this.preset = presets[appTeam][appName][compName]
else
this.preset = presets[appTeam][appName]['default']
else
this.preset = presets[appTeam]['default']
else
this.preset = presets['default']
Thank you in advance

One way would be to use "inject" to walk along your keys, and keep track of the latest default seen... Something like this:
def value(Map presets, String... keys) {
keys.inject([pos: presets, result: presets.default]) { latest, current ->
def node = latest.pos[current]
if (node instanceof Map) {
[pos: node, result: node.default ?: latest.result]
} else if (node instanceof String) {
[pos: node, result: node]
} else if (node == null) {
[pos: node, result: latest.result]
} else {
throw new RuntimeException("No idea what to do with a ${node.class.simpleName}")
}
}.result
}
Then you can do (with the above example)
// Defaults
assert value(presets, 'TeamUnknown', "AppName2", "Component14") == 'preset_default'
assert value(presets, 'Team1', "AppName2", "Component14") == 'preset_team1Default'
// Values
assert value(presets, 'Team1', "AppName2", "Component1") == 'preset_team1App2Comp1'

another way
def value(presets, team, app, compt) {
def defkey = 'default'
return presets.get(team)?.get(app)?.get(compt) ?:
presets.get(team)?.get(app)?.get(defkey) ?:
presets.get(team)?.get(defkey) ?:
presets.get(defkey)
}
assert value(presets, 'TeamUnknown', "AppName2", "Component14") == 'preset_default'
assert value(presets, 'Team1', "AppName2", "Component14") == 'preset_team1Default'
assert value(presets, 'Team1', "AppName2", "Component1") == 'preset_team1App2Comp1'

A simple generic recursion would help:
def input = new groovy.json.JsonSlurper().parseText '''{
"presets": {
"default": "preset_default",
"Team1":{
"AppName1":{
"Component1": "preset_team1App1Comp1",
"default" : "preset_team1App1Default"
},
"AppName2":{
"Component1": "preset_team1App2Comp1",
"Component2": "preset_team1App2Comp2"
},
"default" : "preset_team1Default"
},
"Team2":{
"AppName1":{
"Component1": "preset_team2App1Comp1"
}
}
}
}'''
def findRecursive
findRecursive = { json, List keys, List defaults = [] ->
if( !json || ( json in String ) || !keys ) return json
if( json.default ) defaults << json.default
findRecursive json[ keys.remove( 0 ) ] ?: defaults.last(), keys, defaults
}
assert 'preset_team1App1Comp1' == findRecursive( input.presets, [ 'Team1', 'AppName1', 'Component1' ] )
assert 'preset_team2App1Comp1' == findRecursive( input.presets, [ 'Team2', 'AppName1', 'Component1' ] )
assert 'preset_team1Default' == findRecursive( input.presets, [ 'Team1', 'AppName3', 'Component1' ] )
assert 'preset_default' == findRecursive( input.presets, [ 'Team3' ] )
assert 'preset_default' == findRecursive( input.presets, [ 'Team2', 'AppName2', 'Component1' ] )

Related

how I could create a metaclass that work with string ; numbers and null values

a metaclass that works with number value string value and null value
like this code ; please help me
String.metaClass.formatx = { delegate.toString().replaceAll(/null/, '0.0').toFloat() }
m= "4".formatx()
m2=4.formatx()
m3=null.formatx()
If I were you, I'd do the following:
String.metaClass.formatx = { -> delegate.toFloat() }
String a = "3"
String b = null
assert 3.0f == (a?.formatx() ?: 0.0f)
assert 0.0f == (b?.formatx() ?: 0.0f)
That is, defend against null in your code with ?. and ?:
If you have to try and catch the null, and format it, you could do:
import org.codehaus.groovy.runtime.NullObject
String.metaClass.formatx = { -> delegate.toFloat() }
NullObject.metaClass.formatx = { -> 0.0f }
String a = "3"
String b = null
assert 3.0f == a.formatx()
assert 0.0f == b.formatx()
But adding a method to NullObject feels wrong, and I've never done it before
Edit
This is shorter
import org.codehaus.groovy.runtime.NullObject
[String, Integer].each { it.metaClass.formatx = { -> delegate.toFloat() } }
NullObject.metaClass.formatx = { -> 0.0f }
println null.formatx()
println 3.formatx()
println "4".formatx()
I put this for example but I think THat I use much code
I repeat metaClass.formatx three times ; I dont know if is possible use OR setences INSTEAD
import org.codehaus.groovy.runtime.NullObject
String.metaClass.formatx = { -> delegate.toString().replaceAll(/null/, '0.0').toFloat() }
NullObject.metaClass.formatx = { -> delegate.toString().replaceAll(/null/, '0.0').toFloat() }
Integer.metaClass.formatx = { -> delegate.toString().replaceAll(/null/, '0.0').toFloat() }
m2= 4.formatx()
m= "4".formatx()
println null.formatx()
edit
import org.codehaus.groovy.runtime.NullObject
[String, Integer,NullObject].each { it.metaClass.formatx = { -> delegate.toString().replaceAll(/null/, '0.0').toFloat() } }
m2= 4.formatx()
m= "4".formatx()
println null.formatx()

Return object with dynamic keys in AQL

Can I return something like:
{
"c/12313" = 1,
"c/24223" = 2,
"c/43423" = 3,
...
}
from an AQL query? The idea is something like (this non-working code):
for c in my_collection
return { c._id : c.sortOrder }
where sortOrder is some property on my documents.
Yes, it is possible to have dynamic attribute names:
LET key = "foo"
LET value = "bar"
RETURN { [ key ]: value } // { "foo": "bar" }
An expression to compute the attribute key has to be wrapped in [ square brackets ], like in JavaScript.
This doesn't return quite the desired result however:
FOR c IN my_collection
RETURN { [ c._id ]: c.sortOrder }
[
{ "c/12313": 1 },
{ "c/24223": 2 },
{ "c/43423": 3 },
...
]
To not return separate objects for every key, MERGE() and a subquery are required:
RETURN MERGE(
FOR c IN my_collection
RETURN { [ c._id ]: c.sortOrder }
)
[
{
"c/12313": 1,
"c/24223": 2,
"c/43423": 3,
...
}
]

Return Nested Key in Groovy

I am trying to determine the best way to return nested key values using groovy. If I have a map:
def map = [
OrganizationName: 'SampleTest',
Address: [
Street: '123 Sample St',
PostalCode: '00000',
]
]
Is there a way to return all of the keys? OrganizationName, OrganizationURL, Address.Street, Address.PostalCode? If I didn't have an map within a map I could use map.keySet() as String[]. Should I just loop through each key and see if it is an instanceof another map?
The Groovy libraries don't provide a method for this, but you can write your own. Here's an example that you can copy-paste into the Groovy console
List<String> getNestedMapKeys(Map map, String keyPrefix = '') {
def result = []
map.each { key, value ->
if (value instanceof Map) {
result += getNestedMapKeys(value, keyPrefix += "$key.")
} else {
result << "$keyPrefix$key"
}
}
result
}
// test it out
def map = [
OrganizationName: 'SampleTest',
Address: [
Street: '123 Sample St',
PostalCode: '00000',
]
]
assert ['OrganizationName', 'Address.Street', 'Address.PostalCode'] == getNestedMapKeys(map)
Use Following generic recursion Method To generate the list of all nested map keys
def getListOfKeys(def map, String prefix,def listOfKeys){
if(map instanceof Map){
map.each { key, value->
if(prefix.isEmpty()){
listOfKeys<<key
}else{
listOfKeys<< prefix+"."+key
}
if(value instanceof List){
List list = value
list.eachWithIndex { item, index ->
if(prefix.isEmpty()){
getListOfKeys(item, key+"["+index+"]",listOfKeys)
}else{
getListOfKeys(item, prefix+"."+key+"["+index+"]",listOfKeys)
}
}
}else if(value instanceof Map){
if(prefix.isEmpty()){
getListOfKeys(value, key,listOfKeys)
}else{
getListOfKeys(value, prefix+"."+key,listOfKeys)
}
}
}
}
}
call above method as follows
def void findAllKeysInMap(){
Map map = [ "fields":[
"project":
[
"key": "TP"
],
"summary": "Api Testing Issue.",
"description": "This issue is only for api testing purpose",
"issuetype": [
"name": ["Bug":["hello":[["saurabh":"gaur","om":"prakash"], ["gaurav":"pandey"], ["mukesh":"mishra"]]]]
]
]
]
def listOfKeys=[]
getListOfKeys(map, '', listOfKeys)
println "listOfKeys>>>"+listOfKeys
}
output:
listOfKeys>>>[fields, fields.project, fields.project.key, fields.summary, fields.description, fields.issuetype, fields.issuetype.name, fields.issuetype.name.Bug, fields.issuetype.name.Bug.hello, fields.issuetype.name.Bug.hello[0].saurabh, fields.issuetype.name.Bug.hello[0].om, fields.issuetype.name.Bug.hello[1].gaurav, fields.issuetype.name.Bug.hello[2].mukesh]
There's no such method You're looking for in groovy. You need to do it using instanceof and probably a recursive method.
Slightly shorter:
String key = 'Address.Street'
key.split('\\.').inject(yourMap) {map, path -> map[path]}
If you can't guarantee that the path exists and is complete (say, if you tried to access OrganizationName.id) you'll need to add some safeguards (check that map is not null, and that it's really a Map)

Groovy MarkupBuilder passing string by reference

Doing transformation of a JSON feed to a format that can be consumed by our Endeca Instance, and settled on writing this transformation in Groovy, due to tools like JsonSlurper and MarkupBuilder. Our JSON feed input looks like this (saved as stage/newObject.json):
[
{
"Name": "Object Name",
"DimID": 0000,
"ObjectID": "Object-0000",
"TypeID": 1,
"Type": "Object Type",
"Description": "A Description",
"DirectorID": "007",
"DirectorName": "Bond, James",
"ParentObjectID": null,
"ObjectLevelID": 1,
"ObjectLevel": "MI-6",
"ProjectNumbers": [
"0001",
"0002"
],
"ManagerIDs": [
"001"
],
"ManagerNames": [
"M"
],
"SubObjectIDs": [
"006",
"005"
],
"TaskNumbers": [
"001"
],
"ProjectNames": [
"Casino Royal",
"Goldfinger",
"License to Kill"
]
}
]
And the code I've got to do the transform is this:
def rawJSONFile = new File("stage/newObject.json")
JsonSlurper slurper = new JsonSlurper()
def slurpedJSON = slurper.parseText(rawJSONFile.text)
def xmlOutput = new MarkupBuilder(new FileWriter(new File("stage/ProcessedOutput.xml")))
xmlOutput.RECORDS() {
for (object in slurpedJSON) {
RECORD() {
for (field in object) {
if (field.value != null) {
if (field.value.value.class.equals(java.util.ArrayList)) {
if (field.value.value.size > 0) {
for (subField in field.value.value) {
if (subField != null) {
PROP(NAME: field.key) {
PVAL(subField)
}
}
}
}
} else {
PROP(NAME: field.key) {
PVAL(field.value)
}
}
}
}
}
}
}
The issue we're experiencing is about half way down the groovy script, where it's dealing with sub fields (that is the arrays within the JSON), the closure that's creating the "PVAL" node is passing the subField variable by reference and it's not being treated as a string but a character array, so trying to do the output, we get a Memory location, rather than a String. The workaround we've got so far is this, but I wanted to know if there was a more elegant solution:
for (subField in field.value.value) {
if (subField != null) {
PROP(NAME: field.key) {
StringBuilder subFieldValue = new StringBuilder();
for(int i =0; i<subField.length; i++){
subFieldValue.append(subField[i])
}
PVAL(subFieldValue.toString())
}
}
}
Change subField in field.value.value to subField in field.value in
for (subField in field.value) {
if (subField != null) {
PROP(NAME: field.key) {
PVAL(subField)
}
}
}
Although this logic can be simplified as
xmlOutput.RECORDS {
slurpedJSON.each { map ->
Record {
map.each { k, v ->
if ( v in ArrayList ) {
v.each { val ->
PROP(NAME: k) {
PVAL(val)
}
}
} else {
PROP(NAME: k) {
PVAL(v)
}
}
}
}
}
}

Groovy writing single method insted of two each function

I am a Groovy beginner
These are two each calls which both do a similar task but with a different property of s2PublicLifecycleInstance.
How can I write a separate function which does the following, so that I can have only method call to achive the task?
def scheduledStatusArray =[]
def publishedStatusArray =[]
s2PublicLifecycleInstance.each {
if(it.scheduledInMyLearnStatus == "No"){
scheduledStatusArray.add(1)
} else if(it.scheduledInMyLearnStatus == "Yes"){
scheduledStatusArray.add(2)
}else{
scheduledStatusArray.add(3)
}
}
s2PublicLifecycleInstance.each {
if(it.publishedOnTrainingClassesStatus == "No"){
publishedStatusArray.add(1)
}else if(it.publishedOnTrainingClassesStatus == "Yes"){
publishedStatusArray.add(2)
}else{
publishedStatusArray.add(3)
}
}
I want something like this:
def publishedStatusArray = functionCall(s2PublicLifecycleInstance, propertyName)
is it possible in Groovy?
You could use closures and currying:
def checkStatus = { prop,instances ->
def statuses = instances.collect {
switch( it[prop] ) {
case "Yes": 1
break
case "No": 2
break
default: 3
}
}
return statuses
}
def checkScheduledStatus = checkStatus.curry("scheduledInMyLearnStatus")
def checkPublishedStatus = checkStatus.curry("publishedOnTrainingClassesStatus")
def scheduledStatusArray = checkScheduledStatus(s2PublicLifecycleInstance)
def publishedStatusArray = checkPublishedStatus(s2PublicLifecycleInstance)
You can do it this way using a method:
List check( List lifecycle, String param ) {
lifecycle.collect { it ->
switch( it."$param" ) {
case "No":
return 1
case "Yes":
return 2
default:
return 3
}
}
}
def scheduledStatusArray = check( s2PublicLifecycleInstance, 'scheduledInMyLearnStatus' )
def publishedStatusArray = check( s2PublicLifecycleInstance, 'publishedOnTrainingClassesStatus' )
Or, you could do this (which is less code):
List check( List lifecycle, String param ) {
def retvals = [ 'No':1, 'Yes':2 ].withDefault { 3 }
lifecycle.collect { it ->
retvals[ it."$param" ]
}
}
Another alternative would be to use inject with multiple assignment:
def (scheduledStatusArray, publishedStatusArray) = s2PublicLifecycleInstance.inject( [[],[]] ) { lists, it ->
lists[ 0 ] << ( it.scheduledInMyLearnStatus == 'No' ? 1 :
it.scheduledInMyLearnStatus == 'Yes' ? 2 : 3 )
lists[ 1 ] << ( it.publishedOnTrainingClassesStatus == 'No' ? 1 :
it.publishedOnTrainingClassesStatus == 'Yes' ? 2 : 3 )
lists
}
def myMethod(param, arrayToAdd) {
if(param == "No"){
arrayToAdd.add(1)
}else if(param == "Yes"){
arrayToAdd.add(2)
}else{
arrayToAdd.add(3)
}
}
s2PublicLifecycleInstance.each {myMethod(it.scheduledInMyLearnStatus, scheduledStatusArray)}
s2PublicLifecycleInstance.each {myMethod(it.publishedOnTrainingClassesStatus, publishedStatusArray)}
I agree with gasan. I can add, that
def publishedStatusArray = functionCall(s2PublicLifecycleInstance, propertyName)
is possible in groovy. The function:
functionCall(def s2PublicLifecycleInstance, String propertyName){
s2PublicLifecycleInstance."$propertyName"
}
will return the property you want.

Resources