Get Only unique values from an array - groovy

I have an array of arrays and I need to get only unique values empname and dept from that array without depending on hoursWorked.
def data = [[empname:'Test1',dept:10,hoursWorked:6],
[empname:'Test1',dept:10,hoursWorked:2],
[empname:'Test2',dept:10,hoursWorked:10]] as Set
println data;
I used Set so that it contains only unique values. Since hoursWorked is different I am getting all the three values.
I want the expected result to be:
[[empname:Test1, dept:10],[empname:Test2, dept:10]]

You can cast a list of maps to a set of maps, but first, you need to transform the list to contain only the map entries you are interested in. Consider the following example:
data.collect { [empname: it.empname, dept: it.dept] }.toSet()
In the first run, it transforms each map to a map that contains only the two keys, and after that, it transforms a list to a set, so it contains only unique pairs of empname and dept.
It produces expected output:
[[empname:Test1, dept:10], [empname:Test2, dept:10]]

Yeah, that won't work as the values are different.
You can write a comparison closure for your data and pass it to unique
Take care though, as unique mutates the original list
def data = [
[empname:'Test1',dept:10,hoursWorked:6],
[empname:'Test1',dept:10,hoursWorked:2],
[empname:'Test2',dept:10,hoursWorked:10]
]
println data.unique { a, b -> a.empname <=> b.empname ?: a.dept <=> b.dept }
// Prints: [[empname:Test1, dept:10, hoursWorked:6], [empname:Test2, dept:10, hoursWorked:10]]
println data
// Prints: [[empname:Test1, dept:10, hoursWorked:6], [empname:Test2, dept:10, hoursWorked:10]]
// Original collection was mutated

You can use unique with a closure returning values that are only based on the keys you want to use:
data.unique{[it['empname'], it['dept']]}
===> [[empname:Test1, dept:10, hoursWorked:6],
[empname:Test2, dept:10, hoursWorked:10]]
data.unique{[it['empname'], it['dept']]} will let Groovy compute a collection of distinct values based on the empname and dept keys only. It's up to you to discard the hoursWorked keys.

Related

How to combine and sort key-value pair in Terraform

since the last update of the Logicmonitor provider in Terraform we're struggling with a sorting isse.
In LogicMonitor the properties of a device are a name-value pair, and they are presented alfabetically by name. Also in API requests the result is alphabetical. So far nothing fancy.
But... We build our Cloud devices using a module. Calling the module we provide some LogicMonitor properties specially for this device, and a lot more are provided in the module itself.
In the module this looks like this:
`
custom_properties = concat([
{
name = "host_fqdn"
value = "${var.name}.${var.dns_domain}"
},
{
name = "ocid"
value = oci_core_instance.server.id
},
{
name = "private_ip"
value = oci_core_instance.server.private_ip
},
{
name = "snmp.version"
value = "v2c"
}
],
var.logicmonitor_properties)
`
The first 4 properties are from the module and combined with anyting what is in var.logicmonitor_properties. On the creation of the device in LogicMonitor all properties are set in the order the are and no problem.
The issue arises when there is any update on a terraform file in this environment. Due to the fact the properties are presented in alphabetical order, Terraform is showing a lot of changes if finds (but which are in fact just a mixed due to sorting).
The big question is: How can I sort the complete list of properties bases on the "name".
Tried to work with maps, sort and several other functions and examples, but got nothing working on key-value pairs. Merging single key's works fine in a map, but how to deal with name/value pairs/
I think you were on the right track with maps and sorting. Terraform maps do not preserve any explicit ordering themselves, and so whenever Terraform needs to iterate over the elements of a map in some explicit sequence it always do so by sorting the keys lexically (by Unicode codepoints) first.
Therefore one answer is to project this into a map and then project it back into a list of objects again. The projection back into list of objects will implicitly sort the map elements by their keys, which I think will get the effect you wanted.
variable "logicmonitor_properties" {
type = list(object({
name = string
value = string
}))
}
locals {
base_properties = tomap({
host_fqdn = "${var.name}.${var.dns_domain}"
ocid = oci_core_instance.server.id
private_ip = oci_core_instance.server.private_ip
"snmp.version" = "v2c"
})
extra_properties = tomap({
for prop in var.logicmonitor_properties : prop.name => prop.value
})
final_properties = merge(local.base_properties, local.extra_properties)
# This final step will implicitly sort the final_properties
# map elements by their keys.
final_properties_list = tolist([
for k, v in local.final_properties : {
name = k
value = v
}
])
}
With all of the above, local.final_properties_list should be similar to the custom_properties structure you showed in your question except that the elements of the list will be sorted by their names.
This solution assumes that the property names will be unique across both base_properties and extra_properties. If there are any colliding keys between both of those maps then the merge function will prefer the value from extra_properties, overriding the element of the same key from base_properties.
First, use the sort() function to sort the keys in alphabetical order:
sorted_keys = sort(keys(var.my_map))
Next, use the map() function to create a new map with the sorted keys and corresponding values:
sorted_map = map(sorted_keys, key => var.my_map[key])
Finally, you can use the jsonencode() function to print the sorted map in JSON format:
jsonencode(sorted_map)```

Hazelcast Predicate in clause

Can anyone tell me how to 'in' clause with Hazelcast predicate. I want to use the following,
Predicates.in('column',value);
I want to pass the value as an ArrayList of values. But it is throwing error as it expects object that implements comparable. Is there any workaround for this?
Predicates.in takes arguments of types (String, Comparable...).
So for the column name you pass a String as you have done.
Comparable... means you can pass the individual values in a comma-separated list, but you can also pass them in an array. An ArrayList won't automatically be converted, but you can do it as follows:
ArrayList<String> values = new ArrayList<>(Arrays.asList("one", "two", "three");
Predicates.in("column", values.toArray(new String[]));
The (new String[]) argument is just to ensure you get back an array of Strings, otherwise you'll get an array of Objects.

[ArangoDB]: Get the first element of an object?

I am trying to get the first element of the object scan. In my case, the first element's key changes. So I cannot call it with the key. Here is the AQL query I'm using, which is not working.
`FOR d in collection RETURN DISTINCT Object.keys(d.out.scan)[0]`
Object structure:
{
"out": {
"scan":{
"someKeyThatChanges":"someValue"
}
}
}
Is there a way to fetch the first key of scan?
Thank you
The relevant AQL functions for this issue are documented at
https://docs.arangodb.com/3.3/AQL/Functions/Document.html
In brief, if the object has only one user-defined key, then you will
be able to use VALUES(_, true) directly.
Otherwise, you could use ATTRIBUTES() to get an array of the object's
attributes. You may want to filter it to avoid keys with names that start with "_". Once you've selected a key, remember:
Attributes can also be accessed using the [] accessor
... the square brackets allow for expressions:
... u[attr1][0][attr2][ CONCAT("fir", "st") ]
Demo
LET x = {
"out": {
"scan":{
"someKeyThatChanges":"someValue"
}
}
}
LET y = x.out.scan
LET z = y[ ATTRIBUTES(y)[0] ]
RETURN z
To fetch just the name of the first key of out.scan, the following will work:
FOR d IN collection
RETURN ATTRIBUTES(d.out.scan)[0]
For returning the mapped value for that key, please refer to the other answer given.

dict comprehension with list as value

I have a program that prompts the user for three pieces of input:
Student No.
Name
Age
I then populate a list with this info. This currently works fine.
I was wanting to change the list construct to a dictionary that has the Student No. as the key and a list with the Name and Age as values. I also want the list in case I decide to enlarge the number of inputs in the future.
I was wondering if I could use a dict comprehension but I can't find any examples of using a dict comp being used to create a dict with a list as the value.
The resulting dictionary would look like:
st_dict = {101: [Mark, 54],
102: [Bob, 49]
}
Is it possible, using a dict comp, to create this structure?
Basically I am wanting to do this:
st_dict = {Student_no: [Name, Age] per student entered}

GORM - Update object without retrieving it first

I'd like to be able to update a previously persisted object for which I have an id without having to retrieve it first. The main thing that I'm trying to avoid is having to copy multiple values into the object's fields when that object has been retrieved from the database. I have these values in a map with keys corresponding to the field names so it's trivial to create the object via a constructor with the map as an argument. Unfortunately, an object created this way results in a new database record when saved even though the id field is set to that of an existing record.
I'm currently using a slight variation on one of the examples shown here for copying Groovy class properties but it's not a very elegant solution for multiple reasons.
Basically I'd like to be able to do something like this:
class Foo {
int a
String b
}
def data = [id: 99, a: 11, b: "bar"] //99 is the id of an existing record
def foo = new Foo(data)
foo.update() //or some other comparable persistence mechanism
Thanks
As long as your map keys have the same name as your object properties, you can use executeUpdate without specifying the individual property names with a closure or function like the following:
def updateString = { obj, map ->
def str = ""
map.each { key, value ->
str += "${obj}.${key}=:${key},"
}
return str[0..-2]
}
def data= [foo:"bar", machoMan:"RandySavage"]
In this case, println updateString("f", data) returns "f.foo=:foo,f.machoMan=:machoMan".
Then you can do this:
Foo.executeUpdate("update Foo f set ${updateString("f", data)}", data)
Or of course you could combine that all together into one closure or function.
You can use the executeUpdate method on the GORM domain class:
Foo.executeUpdate("update Foo f set f.a=:a, f.b=:b where f.id=:id", data)

Resources