How to match values in arrays and match result against key/val pair - groovy

I'm using Groovy.
I've got two sets of data. The first is an array of site codes and the second is a key/val map of some JSON data.
I need to loop through the list of site codes and match them to key in the map. Once it finds a match it needs to return the corresponding map val.
The map array looks like this:
list = ["WSM-3572", "WSM-0301","WSM-10153"]
A keypair looks like this:
{id=3dd9794a-d148-4f74-a297-cefe22d05cfd, name=Nedbank Mall of Africa(WSM-3572)},{id=8fb57fda-8bdf-4aef-8d50-f3bf8d2235e1, name=Caffe Rossini (WSM-3432)},
{id=bd12b3ef-b72f-4211-8987-2e0c6f1f688d, name=Steers Welkom (WSM-4502)},
So in the above case we should run through the list and when it gets to WSM-3572 it should find it and match the site code in the name: Nedbank Mall of Africa(WSM-3572) and then return id=3dd9794a-d148-4f74-a297-cefe22d05cfd.
I hope this all makes sense and thanks in advance

Assuming you've loaded your json into a map with JsonSlurper, something like
list.each { code ->
println "$code = " + json.find { it ->
it.name.contains "($it)"
}?.id
}
Should do it.
Not at a computer, but that should be close

Related

Remove a map item in terraform

If I use the following yamldecode, I get the following output:
output "product" {
value = distinct(yamldecode(file("resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"])
}
Output:
+ product = [
+ "fargate",
+ "CRM",
]
I want fargate to be removed from my output and the expected output is this:
+ product = [
+ "CRM"
]
Please let me know how I can do this.
output "product" {
value = compact([for x in distinct(yamldecode(file("resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"]) : x == "fargate" ? "" : x])
}
I get this output:
test = [
"enter",
]
compact function solved the problem.
The Terraform language is based on functional principles rather than imperative principles and so it does not support directly modifying an existing data structure. Instead, the best way to think about goals like this is how to define a new value which differs from your existing value in some particular way.
For collection and structural types, the most common way to derive a new value with different elements is to use a for expression, which describes a rule for creating a new collection based on the elements of an existing collection. For each element in the source collection we can decide whether to use it in the result at all, and then if we do decide to use it we can also describe how to calculate a new element value based on the input element value.
In your case you seem to want to create a new list with fewer elements than an existing list. (Your question title mentions maps, but the question text shows a tuple which is behaving as a list.)
To produce a new collection with fewer elements we use the optional if clause of a for expression. For example:
[for x in var.example : x if x != "fargate"]
In the above expression, x refers to each element of var.example in turn. Terraform first evaluates the if clause, and expects it to return true if the element should be used. If so, it will then evaluate the expression immediately after the colon, which in this case is just x and so the result element is identical to the input element.
Your example expression also includes some unrelated work to decode a YAML string and then traverse to the list inside it. That expression replaces var.example in the above expression, as follows:
output "products" {
value = toset([
for x in yamldecode(file("${path.module}/resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"]: x
if x != "fargate"
])
}
I made some other small changes to the above compared to your example:
I named the output value "products" instead of "product", because it's returning a collection and it's Terraform language idiom to use plural names for collection values.
I wrapped the expression in toset instead of distinct, because from your description it seems like this is an unordered collection of unique strings rather than an ordered sequence of strings.
I added path.module to the front of the file path so that this will look for the file in the current module directory. If your output value is currently in a root module then this doesn't really matter because the module directory will always be the current working directory, but it's good practice to include this so that it's clear that the file belongs to the module.
Therefore returning a set is more appropriate than returning a list because it communicates to the caller of the module that they may not rely on the order of these items, and therefore allows the order to potentially change in future without it being a breaking change to your module.

groovy iterate through list of key and value

I have this list:
service_name_status=[a-service=INSTALL, b-service=UPGRADE, C-service=UPGRADE, D-service=INSTALL]
And I need to iterate through this list so the first element will be the value of a parameter called "SERVICE_NAME" and the second element will be the value of a parameter called "HELM_COMMAND",
after asserting those values to the parameters I will run my command that uses those parameters and then continue the next items on the list which should replace the values of the parameters with items 3 and 4 and so on.
So what I am looking for is something like that:
def service_name_status=[a-service=INSTALL, b-service=UPGRADE, C-service=UPGRADE, D-service=INSTALL]
def SERVICE_NAME
def HELM_COMMAND
for(x in service_name_status){
SERVICE_NAME=x(0,2,4,6,8...)
HELM_COMMAND=x(1,3,5,7,9...)
println SERVICE_NAME=$SERVICE_NAME
println HELM_COMMAND=$HELM_COMMAND
}
the output should be:
SERVICE_NAME=a-service
HELM_COMMAND=INSTALL
SERVICE_NAME=b-service
HELM_COMMAND=UPGRADE
SERVICE_NAME=c-service
HELM_COMMAND=UPGRADE
SERVICE_NAME=d-service
HELM_COMMAND=INSTALL
and so on...
I couldn't find anything that takes any other element in groovy, any help will be appreciated.
The collection you want is a Map, not a List.
Take note of the quotes in the map, the values are strings so you need the quotes or it won't work. You may have to change that at the source where your data comes from.
I kept your all caps variable names so you will feel at home, but they are not the convention.
Note the list iteration with .each(key, value)
This will work:
Map service_name_status = ['a-service':'INSTALL', 'b-service':'UPGRADE', 'C-service':'UPGRADE', 'D-service':'INSTALL']
service_name_status.each {SERVICE_NAME, HELM_COMMAND ->
println "SERVICE_NAME=${SERVICE_NAME}"
println "HELM_COMMAND=${HELM_COMMAND}"
}
EDIT:
The following can be used to convert that to a map. Be careful, the replaceAll part is fragile and depends on the data to always look the same.
//assuming you can have it in a string like this
String st = "[a-service=INSTALL, b-service=UPGRADE, C-service=UPGRADE, D-service=INSTALL]"
//this part is dependent on format
String mpStr = st.replaceAll(/\[/, "['")
.replaceAll(/=/, "':'")
.replaceAll(/]/, "']")
.replaceAll(/, /, "', '")
println mpStr
//convert the properly formatted string to a map
Map mp = evaluate(mpStr)
assert mp instanceof java.util.LinkedHashMap

Filter the list generated by a Gremlin traversal and Groovy

I'm doing the following traversal:
g.V().has('Transfer','eventName','Airdrop').as('t1').
outE('sent_to').
inV().dedup().as('a2').
inE('sent_from').
outV().as('t2').
where('t1',eq('t2')).by('address').
outE('sent_to').
inV().as('a3').
select('a3','a2').
by('accountId').toList().groupBy { it.a3 }.collectEntries { [(it.key): [a2 : it.value.a2]]};
So as you can see I'm basically doing a traversal and at the end I'm using groovy with collectEntries to aggregate the results like I need them, which is aggregated by a3 in this case. The results look like this:
==>0xfe43502662ce2adf86d9d49f25a27d65c70a709d={a2=[0x99feb505a8ed9976cf19e757a9536117e6cdc5ba, 0x22019ad32ea3adabae68003bdefd099d7e5e3886]}
(This is GOOD, because the number of values in a2 is at least 2)
==>0x129e0131ea3cc16fe5252d7280bd1258f629f20f={a2=[0xf7958fad496d15cf9fd9e54c0012504f4fdb96ff]}
(This is NOT GOOD, I want to return in my list only those combinations where there are at lest 2 values for a2)
I have tried using filters and an additional where step in the traversal itself but I haven't been able to do it. I'm not sure if this is something I should skip using Groovy in my last line. Any help or orientation would be very much appreciated
I don't think you need to drop into Groovy to get the answer you want. It would be preferable to do this all in Gremlin especially since you intend to filter results which could yield some performance benefit. Gremlin has it's own group() step as well as methods for filtering the resulting Map:
g.V().has('Transfer','eventName','Airdrop').as('t1').
out('sent_to').
dedup().as('a2').
in('sent_from').as('t2').
where('t1',eq('t2')).by('address').
out('sent_to').inV().as('a3').
select('a3','a2').
by('accountId').
group().
by('a3').
by('a2').
unfold().
where(select(values).limit(local,2).count(local).is(gte(2)))
The idea is to build your Map with group() then deconstruct it to entries with unfold(). You the filter each entry with where() by selecting the values of the entry, which is a List of "a2" then counting the items locally in that List. I use limit(local,2) to avoid unnecessary iteration beyond 2 since the filter is gte(2).
The easiest way to do this is with findAll { }.
.groupBy { it.a3 }
.findAll { it.value.a2.size() > 1 }
.collectEntries { [(it.key): [a2: it.value.a2]] }
if some a2 are null, then value.a2 also evaluates to null and filters the results without the need for explicit nullchecks

Convert sql.GroovyRowResult with two columns to map

I have a groovy GroovyRowResult (using sql.rows) with two columns. The first column has a group by in the sql, so it will always be unique.
Now I would like to convert the whole result into a map, which contains the first column value as the key and the second column value as the value. I know that I can do this with each for every single row (see below), but was wondering if there is a better aproeach. The only thing I've found is entrySet(), which did not work. Error is:
Message: No signature of method:
java.util.ArrayList.entrySet() is applicable for argument types: () values: [])
So my each row way would be:
def myMap = [:]
result.each {
myMap.put(it.colum1, it.column2)
}
Another alternative would be, to make the query in a way that will return me a map with the first column as the key and the second as the value (or the whole row as the value). Kind of Zend frameworks fetchAssoc() method.
You could do:
def myMap = result.collectEntries {
[ it.column1, it.column2 ]
}

What do empty square brackets after a variable name mean in Groovy?

I'm fairly new to groovy, looking at some existing code, and I see this:
def timestamp = event.timestamp[]
I don't understand what the empty square brackets are doing on this line. Note that the timestamp being def'd here should receive a long value.
In this code, event is defined somewhere else in our huge code base, so I'm not sure what it is. I thought it was a map, but when I wrote some separate test code using this notation on a map, the square brackets result in an empty value being assigned to timestamp. In the code above, however, the brackets are necessary to get correct (non-null) values.
Some quick Googling didn't help much (hard to search on "[]").
EDIT: Turns out event and event.timestamp are both zero.core.groovysupport.GCAccessor objects, and as the answer below says, the [] must be calling getAt() on these objects and returning a value (in this case, a long).
The square brackets will invoke the underlying getAt(Object) method of that object, so that line is probably invoking that one.
I made a small script:
class A {
def getAt(p) {
println "getAt: $p"
p
}
}
def a = new A()
b = a[]
println b.getClass()
And it returned the value passed as a parameter. In this case, an ArrayList. Maybe that timestamp object has some metaprogramming on it. What does def timestamp contains after running the code?
Also check your groovy version.
Empty list, found this. Somewhat related/possibly helpful question here.
Not at a computer, but that looks like it's calling the method event.timestamp and passing an empty list as a parameter.
The same as:
def timestamp = event.timestamp( [] )

Resources