I've got a database table that looks something like (jsonified
):
[
{
"name": "John",
"date": "2020-01-01",
"height": "3'5"
},
{
"name": "John",
"date": "2021-01-01",
"height": "3'9"
},
{
"name": "Sarah",
"date": "2020-01-01",
"height": "4'0"
},
{
"name": "Sarah",
"date": "2021-01-01",
"height": "4'9"
},
{
"name": "Philip",
"date": "2021-01-01",
"height": "5'5"
}
]
What I want to get to is a dataset that looks like:
{
"John": {
"2020-01-01": "3'5",
"2021-01-01": "3'9"
},
"Sarah": {
"2020-01-01": "4'0",
"2021-01-01": "4'9"
},
"Philip": {
"2020-01-01": "5'5"
}
}
I'm currently using:
{ for (.) .name: {.date:.height} }
But that results in only the last item of each conflict surviving:
{
"John": {
"2021-01-01": "3'9"
},
"Sarah": {
"2021-01-01": "4'9"
},
"Philip": {
"2020-01-01": "5'5"
}
}
JSLT
?JSLT
. JSLT
says it's extensible. Is this possible with an extension?what you did sounds reasonable but its not going to work because in jslt if you try to add the same item to an array the last one will win and its not going to be merged as someone would think.
Also, something to be aware of , you can use the + operator to merge two objects. For example if you do this in jslt {"x":1} + {"y":2} then you will get both x & y in one json object but last merged object will be the first in order , so the result will look like this: {"y":2,"x":1}.
Another important rule that you need to be aware of which took me sometime to realize is if you add two objects with the same key the one in the left will take precedence . For example if you add (merge) two objects as in the following:
{"x":1}+{"x":2} The result will be {"x":1"}. Im not sure why, but this is how the language is designed.
Taking all of the above in consideration, to resolve your issue you can do the following:
def merge-by-name(array,output)
if(size($array) == 0)
{ $array[0].name : get-key( $output,$array[0].name,{}) +
{$array[0].date:$array[0].height}
} + $output
else
let new-output = { $array[-1].name : get-key( $output,$array[-1].name,{}) +
{$array[-1].date: $array[-1].height}
} + $output
merge-by-name($array[0 : -1],$new-output)
merge-by-name(.,{})
Notice that I have used recursion for better efficiency, but You can also use nested loop so that for each element (1st loop) you find matches (2ed loop) and merge together.
Its worth mentioning however, if I have the choice between jolt and jstl , in such problem I would pick jolt since its easier to collapse objects that share common fields and transformation would look like this:
[
{
"operation": "shift",
"spec": {
"*": {
"height": "@(1,name).@(1,date)"
}
}
}
]
There is no set rules on when to use jolt vs jslt. Both have strengths and weaknesses and it takes some experience working with both to figure out which works better against what problem.
Hope that helps.