Build a Map from a Set in Groovy - collections

We have the following legacy data structure of Parent and Child objects of the same type
Parent1(name1,code1,null)
Child11(name11,code11,Parent1)
Child12(name12,code12,Parent1)
Parent2(name2,code2,null)
Child21(name21,code21,Parent2)
Child22(name22,code22,Parent2)
etc.
We have a legacy service available that returns a Set of all the Child objects. The Parent objects are not returned but we can call a getParent() getter for a particular Child in the Set to get its Parent. We need to call this service from a Groovy class, and afterwards build a Map that reflects the original structure
def dataMap = [data:[["name":"name1", "code":"code1",
"children":[["name":"name11", "code":"code11"],
["name":"name12", "code":"code12"]]],
["name":"name2", "code":"code2",
"children":[["name":"name21", "code":"code21"],
["name":"name22", "code":"code22"]]]]]
So basically the Map keys are the Parent (name,code) pairs, and the values are Lists of the respective Child objects' (name,code) pairs (the Map will be rendered to JSON afterwards actually)
Being quite novice to Groovy I could probably solve this using Java syntax, but I wonder whether there is a more concise solution using Groovy specific features? Any ideas are appreciated

So as I understand it, this is the setup you have:
import groovy.transform.*
import groovy.json.*
#TupleConstructor(includeFields=true)
class Node {
String name
String code
private Node parent
String getParentName() { parent?.name }
String getParentCode() { parent?.code }
}
def parent1 = new Node( 'name1', 'code1', null )
def child11 = new Node( 'name11', 'code11', parent1 )
def child12 = new Node( 'name12', 'code12', parent1 )
def parent2 = new Node( 'name2', 'code2', null )
def child21 = new Node( 'name21', 'code21', parent2 )
def child22 = new Node( 'name22', 'code22', parent2 )
// This is returned by a call to your API
Set nodes = [ child11, child12, child21, child22 ]
Then, you can do the following (there are probably other routes, and this will only work for single depth trees)
// Get a set of parent nodes
Set parents = nodes.collect { [ name:it.parentName, code:it.parentCode ] }
// Utility closure to return a name and code in a Map
def format = { Node n ->
[ name: n.name, code: n.code ]
}
// Collect the formatted parents with their formatted children into a Map
def dataMap = [ data:parents.collect { p ->
p + [ children:nodes.findAll {
it.parentName == p.name && it.parentCode == p.code
}.collect { format( it ) } ]
} ]
// Print the JSON representation of this
println new JsonBuilder( dataMap ).toPrettyString()
That should print:
{
"data": [
{
"name": "name2",
"code": "code2",
"children": [
{
"name": "name21",
"code": "code21"
},
{
"name": "name22",
"code": "code22"
}
]
},
{
"name": "name1",
"code": "code1",
"children": [
{
"name": "name11",
"code": "code11"
},
{
"name": "name12",
"code": "code12"
}
]
}
]
}

Related

Pacts: Matching rule for non-empty map (or a field which is not null) needed

I need help with writing my consumer Pacts using pact-jvm (https://github.com/DiUS/pact-jvm).
My problem is I have a field which is a list (an array) of maps. Each map can have elements of different types (strings or sub-maps), eg.
"validatedAnswers": [
{
"type": "typeA",
"answers": {
"favourite_colour": "Blue",
"correspondence_address": {
"line_1": "Main St",
"postcode": "1A 2BC",
"town": "London"
}
}
},
{
"type": "typeB",
"answers": {
"first_name": "Firstname",
"last_name": "Lastname",
}
}
]
but we're only interested in some of those answers.
NOTE: The above is only an example showing the structure of validatedAnswers. Each answers map has dozens of elements.
What we really need is this: https://github.com/pact-foundation/pact-specification/issues/38, but it's planned for v.4. In the meantime we're trying a different approach. What I'm attempting to do now is to specify that each element of the list is a non-empty map. Another approach is to specify that each element of the list is not null. Can any of this be done using Groovy DSL?
This:
new PactBuilder().serviceConsumer('A').hasPactWith('B')
.port(findAvailablePort()).uponReceiving(...)
.willRespondWith(status: 200, headers: ['Content-Type': 'application/json'])
.withBody {
validatedAnswers minLike(1) {
type string()
answers {
}
}
}
doesn't work because it mean answers is expected to be empty ("Expected an empty Map but received Map( [...] )", see also https://github.com/DiUS/pact-jvm/issues/298).
So what I would like to do is something like this:
.withBody {
validatedAnswers minLike(1) {
type string()
answers Matchers.map()
}
}
or:
validatedAnswers minLike(1) {
type string()
answers {
keyLike 'title', notNull()
}
}
or:
validatedAnswers minLike(1) {
type string()
answers notNull()
}
Can it be done?
I would create two separate tests for this, one test for each of the different response shapes and have a provider state for each e.g. given there are type b answers.
This way when you verify on provider side, it will only send those two field types.
The union of the two examples gives a contract that allows both.
You can do it without DSL, sample Groovy script:
class ValidateAnswers {
static main(args) {
/* Array with some samples */
List<Map> answersList = [
[
type: 'typeA',
answers: [
favourite_colour: 'Blue',
correspondence_address: [
line_1: 'Main St',
postcode: '1A 2BC',
town: 'London'
]
]
],
[
type: 'typeB',
answers: [
first_name: 'Firstname',
last_name: "Lastname"
]
],
[
type: 'typeC',
answers: null
],
[
type: 'typeD'
],
[
type: 'typeE',
answers: [:]
]
]
/* Iterating through all elements in list above */
for (answer in answersList) {
/* Print result of checking */
println "$answer.type is ${validAnswer(answer) ? 'valid' : 'not valid'}"
}
}
/**
* Method to recursive iterate through Map's.
* return true only if value is not an empty Map and it key is 'answer'.
*/
static Boolean validAnswer(Map map, Boolean result = false) {
map.each { key, value ->
if (key == 'answers') {
result = value instanceof Map && value.size() > 0
} else if (value instanceof Map) {
validAnswer(value as Map, false)
}
}
return result
}
}
Output is:
typeA is valid
typeB is valid
typeC is not valid
typeD is not valid
typeE is not valid

Observable contains array of ID to call another observable

I have a data structure in firebase
{
"name": "Sample",
"category": ["123456", "789012"]
}
The array of category contains ID which refers to documents in another collection. I can get the above document as Observable. What I really what as the end result is the below data structure
{
"name": "Sample"
"category": [
{
"name": "Category 1"
},
{
"name": "Category 2"
}
]
}
How can I bring this data? I don't think switchMap works for this. If so, can someone give an example of that?
You can try using flatMap and forkJoin. FlatMap allows you to chain multiple async requests together and forkJoin allows you to wait for all observables to return a value before continuing.
And you could wright something like this:
var finalData;
firstRequest('sample').flatMap((data) => {
// assuming data = { name: "Sample", catagory: [ "23123", "31321", ... ] }
finalData = data;
var observables = [];
data.catagory.forEach((c) => {
observable.push(secondRequest(c));
});
return forkJoin(observables);
}).flatMap((results) => {
// assuming results is an array like [ { name: "Catagory 1", ... } ]
finalData.category = results;
return finalData;
});

API Gateway and DynamoDB PutItem for String Set

I can't seem to find how to correctly call PutItem for a StringSet in DynamoDB through API Gateway. If I call it like I would for a List of Maps, then I get objects returned. Example data is below.
{
"eventId": "Lorem",
"eventName": "Lorem",
"companies": [
{
"companyId": "Lorem",
"companyName": "Lorem"
}
],
"eventTags": [
"Lorem",
"Lorem"
]
}
And my example template call for companies:
"companies" : {
"L": [
#foreach($elem in $inputRoot.companies) {
"M": {
"companyId": {
"S": "$elem.companyId"
},
"companyName": {
"S": "$elem.companyName"
}
}
} #if($foreach.hasNext),#end
#end
]
}
I've tried to call it with String Set listed, but it errors out still and tells me that "Start of structure or map found where not expected" or that serialization failed.
"eventTags" : {
"SS": [
#foreach($elem in $inputRoot.eventTags) {
"S":"$elem"
} #if($foreach.hasNext),#end
#end
]
}
What is the proper way to call PutItem for converting an array of strings to a String Set?
If you are using JavaScript AWS SDK, you can use document client API (docClient.createSet) to store the SET data type.
docClient.createSet - converts the array into SET data type
var docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName:table,
Item:{
"yearkey": year,
"title": title
"product" : docClient.createSet(['milk','veg'])
}
};

Get a tree like structure out of path string

I am stuck since 2 days, as I am not to firm with pointers and recursion. I have an array of path like structures, lets say:
s:=[]string {
"a/b/c",
"a/b/g",
"a/d",
}
With a data structure like this:
type Node struct {
Name string `json:"name"`
Children []Node `json:"children"`
}
I would like to end up with something like this:
{
"name": "a",
"children": [
{
"name": "b",
"children": [
{
"name": "c",
"children": []
},
{
"name": "g",
"children": []
}
]
},
{
"name": "d",
"children": []
}
]
}
I tried to build it with a recursion, which works kind of fine, but only for one string (e.g. "a/b/c"), as soon as I try to implement something which should add missing nodes ("g" in "a/b/g") to a tree I am stuck.
I had something like:
func appendChild(root Node, children []string) Node {
if len(children) == 1 {
return Node{children[0], nil}
} else {
t := root
t.Name=children[0]
t.Children = append(t.Children, appendChild(root, children[1:]))
return t
}
}
Could someone point me to an efficient solution?
https://play.golang.org/p/9pER5cwChF
func AddToTree(root []Node, names []string) []Node {
if len(names) > 0 {
var i int
for i = 0; i < len(root); i++ {
if root[i].Name == names[0] { //already in tree
break
}
}
if i == len(root) {
root = append(root, Node{Name: names[0]})
}
root[i].Children = AddToTree(root[i].Children, names[1:])
}
return root
}
Example output (note that I used omitempty on the children field, because I don't like null entries in my JSONs):
[{
"name": "a",
"children": [{
"name": "b",
"children": [{
"name": "c"
}, {
"name": "g"
}]
}, {
"name": "d"
}]
}]
Notable difference from your version:
It operates on a list of nodes instead of the children of a single node. This is important, as your version assumes that all of the trees have the same single root node (a), when this might not be the case. The only way to handle that in your version is to have a "fake" node at the root.
It does NOT reuse the input node. This is one of the primary problems with your code. If len(children) > 1, you update the input node's name, append to it's children, then recurse. This means that every prior level of the slice becomes part of the children. You need to create a new node instead.
It actually searches the tree. You're not searching the tree to see if the item being inserted already exists, so you duplicate nodes (specifically, node b)

ASP.Net Core - Get All data of a post form

I want to save all data of a form.
My form has these elements-
( Using Postman Plugin )
My controller is like this-
[HttpPost]
public async Task<IActionResult> Insert(IFormCollection data)
{
return Ok(data);
}
So, I am getting something like this-
[
{
"key": "user_id",
"value": [
"'12'"
]
},
{
"key": "title",
"value": [
"123"
]
},
{
"key": "text[]",
"value": [
"werwer",
"ghj"
]
}
]
I want to get the value of texts.
So, for this case-
"werwer",
"ghj"
So, I have tried something like this-
foreach (string description in data["text"])
{
// description => empty
}
and also tried this-
data.text
and also-
data->text
But nothing works for me.
Can anyone please help?
Thanks in advance for helping.
Why not loop through each keys and if the key is "text", get the values. Since the value is a comma seperated string, you can call the Split method on that to get an array which contains 2 items( from your sample input).
foreach (string description in data.Keys)
{
if (description.Equals("text"))
{
var v = data[description];
var stringItems = v.Split(',');
foreach (var stringItem in stringItems)
{
//do something with stringItem
}
}
}
BTW, the key should be text, not text[]. Even if you have muliple input fields with the same name "text", when you submit, It will be a single key ("text") with 2 items int he value property
{
"key": "text",
"value": [
"werwer",
"ghj"
]
}

Resources