JSONata array to array manipulation with mapping - jsonata

I need to transform array to array with some extra logic:
Map field name in array if it exists in the mapping object, if not process it as it is in the source
Sum up values of objects with the same name
Remove objects with zero value
for example here is the source json:
{
"additive": [
{
"name": "field-1",
"volume": "10"
},
{
"name": "field-2",
"volume": "10"
},
{
"name": "field-3",
"volume": "0"
},
{
"name": "field-4",
"volume": "5"
}
]
}
object with mapping config(field-1 and field-2 is mapped to the same value):
{
"field-1": "field-1-mapped",
"field-2": "field-1-mapped",
"field-3": "field-3-mapped"
}
and this is the result that I need to have
{
"chemicals": [
{
"name": "field-1-mapped",
"value": 20
},
{
"name": "field-4",
"value": 5
}
]
}
as you can see field-1 and field-2 is mapped to field-1-mapped so the values are summed up, field-3 has 0 value so it is removed and field-4 is passed as it is because it's missing in the mapping.
so my question is: is it possible to make it with JSONata?
I have tried to make it work but I stuck with this lookup function that doesn't return default value when name is missing in mapping:
{
"chemicals": additive # $additive #$.{
"name": $res := $lookup({
"field-1": "field-1-mapped",
"field-2": "field-1-mapped",
"field-3": "field-3-mapped"
}, $additive.name)[ $res ? $res : $additive.name],
"amount": $number($additive.volume),
} [amount>0]
}

Probably easiest to break it down into steps as follows:
(
/* define lookup table */
$table := {
"field-1": "field-1-mapped",
"field-2": "field-1-mapped",
"field-3": "field-3-mapped"
};
/* substitute the name; if it's not in the table, just use the name */
$mapped := additive.{
"name": [$lookup($table, name), name][0],
"volume": $number(volume)
};
/* group by name, and aggregate the volumes */
$grouped := $mapped[volume > 0]{name: $sum(volume)};
/* convert back to array */
{
"chemicals": $each($grouped, function($v, $n) {{
"name": $n,
"volume": $v
}})
}
)
See https://try.jsonata.org/0BWeRcRoZ

Related

Transform array of values to array of key value pair

I have a json data which is in the form of key and all values in a array but I need to transform it into a array of key value pairs, here is the data
Source data
"2022-08-30T06:58:56.573730Z": [
{ "tag": "AC 3 Phase/7957", "value": 161.37313113545272 },
{ "tag": "AC 3 Phase/7956", "value": 285.46869739695853 }
]
}
Transformation looking for
[
{ "tag": "AC 3 Phase/7957",
"ts": 2022-08-30T06:58:56.573730Z,
"value": 161.37313113545272
},
{ "tag": "AC 3 Phase/7956",
"ts": 2022-08-30T06:58:56.573730Z,
"value": 285.46869739695853
}
]
I would do it like this:
$each($$, function($entries, $ts) {
$entries.{
"tag": tag,
"ts": $ts,
"value": value
}
}) ~> $reduce($append, [])
Feel free to play with this example on the playground: https://stedi.link/g6qJGcP

How to get unique entries in a slice of maps golang

How to get unique entries from below JSON in Go. In the output I should get unique key-value pairs.Here in the input there are 2 same keys type and different values for them. So in the output I should get one key as type and combined values for them. And If there are duplicate key-value pairs one pair needs to be removed.
This is my input
{
"attributes": [
{
"name": "name",
"values": [
"name"
]
},
{
"name": "Type",
"values": [
"type1",
"type2"
]
},
{
"name": "Id",
"values": [
"id"
]
},
{
"name": "Type",
"values": [
"type3",
"type4"
]
}
]
}
And output should look like this
{
"attributes": [
{
"name": "name",
"values": [
"name"
]
},
{
"name": "Type",
"values": [
"type1",
"type2",
"type3",
"type4"
]
},
{
"name": "Id",
"values": [
"id"
]
}
]
}
Here is the code I tried to add uniqueness to the name field. But I need values field uniqueness as well
var res []models.Attributes
var attributes []models.Attributes
keys := make(map[string]bool)
for _, item := range attributes {
if _, value := keys[item.Name]; !value {
keys[item.Name] = true
res = append(res, item)
}
}
return res
You are close. Use a map of maps where the outer map represents an attribute and the keys of the inner map are the unique values for that attribute:
m := make(map[string]map[string]bool)
Loop through the data creating maps as needed:
for _, item := range attributes {
values := m[item.Name]
if values == nil {
values = make(map[string]true)
m[item.Name] = values
}
for _, value := range item.Values {
values[value] = true
}
}
Recreate the slice of attributes from the map:
attributes = attributes[:0] // overwrite the attributes
for name, values := range m {
attribute := Attribute{Name: name}
for value := range values {
attribute.Values = append(attribute.Values, value)
}
attributes = append(attributes, attribute)
}
View the code in action on the playground.

NiFi Jolt Specification for array input

I have the following input in Nifi Jolt Specification processor:
[
{
"values": [
{
"id": "paramA",
"value": 1
}
]
},
{
"values": [
{
"id": "paramB",
"value": 3
}
]
}
]
Expected output:
[
{
"id": "paramA",
"value": 1
},
{
"id": "paramB",
"value": 2
}
]
Can you explain how I have to do?
thanks in advance
You want to reach the objects of the values array which are nested within seperate object signs ({}). A "*" notation is needed in order to cross them over per each individual values array, and then use another "*" notation for indexes of those arrays while choosing "" as the counterpart values in order to grab nothing but the sub-objects such as
[
{
"operation": "shift",
"spec": {
"*": {
"values": {
"*": ""
}
}
}
}
]

Jolt - Get values using a list of keys, alternatives to #(2,#)

I need to create a JSON array in order to split it into several jobs with Nifi.
The array needs to be created based on an existing array inside the JSON.
Can't figure out how to dynamically create a reference to another object in the JSON. I want the reference "#(2,#)" to work, but this is not supported.
INPUT
{
"name": "Loki",
"id": "1234",
"loc": "Utgard",
"age": "unknown",
"listitems": [
"name",
"id"
]
}
SPEC (that doesn't work):
[
{
"operation": "shift",
"spec": {
// Loop all listitems
"listitems": {
"*": {
// Get the value of the current item and push to processlist.type array
"#": "processlist[#2].type",
// Here is the problem, I need to get the "top level" value for the current value/key
"#(2,#)": "processlist[#2].value"
}
}
}
}
]
Expected output:
{
"processlist" : [
{
"type" : "name",
"value" : "Loki"
}, {
"type" : "id",
"value" : "1234"
}
]
}
SPEC (that will run but is not correct)
[
{
"operation": "shift",
"spec": {
// Loop all listitems
"listitems": {
"*": {
// Get the value of the current item and push to processlist.type array
"#": "processlist[#2].type",
// Here is the problem, I need to get the top level value for the current value/key
// Forcing this to "name" will at least execute the code
"#(2,name)": "processlist[#2].value"
}
}
}
}
]
Any ideas?
You can proceed one more step by adding "*" key to nest the current spec more while roaming by #(3,&) dynamically as this ampersand represents the incurred key values name and id such as
[
{
"operation": "shift",
"spec": {
"listitems": {
"*": {
"*": {
"#1": "processlist[#3].type",
"#(3,&)": "processlist[#3].value"
}
}
}
}
}
]

UnderscoreJs : objects sortBy with empty attributes

Problem is, I want to sort by price an array of users that might contains empty attributes like this one:
var array = [
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Johan",
"pricing": {
"base_price" : "12",
"price_by_hour" : "5"
}
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Marco"
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Jane",
"pricing": {
"base_price" : "8",
"price_by_hour" : "11"
}
}
];
array = _.sortBy(array, function(item) {
return item.pricing.base_price;
});
console.log(array);
TypeError: Cannot read property 'base_price' of undefined
How can I put the items without the pricing object at the bottom of my list and still sorting it?
In this case, I want to sort the list with Jane first, then Johan, then Marco.
Here's an easy way to do it:
_.sortBy(array, 'pricing.base_price');
When you pass a string as an iteratee to sortBy(), the property() function is used. This function works with property paths and simply returns undefined if the property doesn't exist.
Just put a conditional
array = _.sortBy(array, function(item){
if(item.pricing){
return item.pricing.base_price;
}
});
OK, I just had to return false if the attribute is empty:
array = _.sortBy(array, function(item) {
if(!item.pricing || !item.pricing.base_price){
return -1;
}
return item.pricing.base_price;
});
You need a conditional to avoid getting TypeError. Also you need to cast base_price to Number to get a proper sorting.
array = _.sortBy(array, function(item){
if(item.pricing){
return Number(item.pricing.base_price);
}
});
One alternative would be already initate them as Number.
var array = [
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Johan",
"pricing": {
"base_price" : 12,
"price_by_hour" : 5
}
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Marco"
},
{
"created": "2015-11-27T16:33:46.781Z",
"name": "Jane",
"pricing": {
"base_price" : 8,
"price_by_hour" : 11
}
}
];
array = _.sortBy(array, function(item) {
if(item.pricing){
return item.pricing.base_price;
}
});

Resources