I am looking to Jolt Transform a complex json into below desired json.
Input JSON:
{
"Rating": 1,
"SecondaryRatings": [
{
"Design": 4,
"Price": 2,
"RatingDimension3": 1,
"Arr1": [
{
"Val1": 34
},
{
"Val2": 45
}
]
},
{
"Design": 44,
"Price": 23,
"RatingDimension3": 12,
"Arr1": [
{
"Val1": 56
},
{
"Val2": 22
}
]
}
]
}
Desired Output
[
{
"Design": 4,
"Price": 2,
"RatingDimension3": 1,
"rating-primary": 1,
"Val1": 34,
"Val2": 45
},
{
"Design": 44,
"Price": 23,
"RatingDimension3": 12,
"rating-primary": 1,
"Val1": 56,
"Val2": 22
}
]
I tried following Jolt Spec
[
{
"operation": "shift",
"spec": {
"SecondaryRatings": {
"*": {
"*": {
"*": {
"#(2,Design)": "[&3].Design",
"#(2,Price)": "[&3].Price",
"#(2,RatingDimension3)": "[&3].RatingDimension3",
"Val1": "[&3].Val1",
"Val2": "[&3].Val2",
"#(4,Rating)": "[&3].rating-primary"
}
}
}
}
}
}
]
But got following Output
[
{
"Design" : [ 4, 4, 4, 4, 4 ],
"Price" : [ 2, 2, 2, 2, 2 ],
"RatingDimension3" : [ 1, 1, 1, 1, 1 ],
"rating-primary" : [ 1, 1, 1, 1, 1 ],
"Val1" : 34,
"Val2" : 45
},
{
"Design" : [ 44, 44, 44, 44, 44 ],
"Price" : [ 23, 23, 23, 23, 23 ],
"RatingDimension3" : [ 12, 12, 12, 12, 12 ],
"rating-primary" : [ 1, 1, 1, 1, 1 ],
"Val1" : 56,
"Val2" : 22
}
]
So as it is seen except for the last level values, all others are having array with repeated values. Can anybody help to what is missing or wrong in Jolt Spec?
You can directly walk through indexes of the outer array(SecondaryRatings) rather than inner one(Arr1) such as
[
{
"operation": "shift",
"spec": {
"SecondaryRatings": {
"*": {
"*": "[&1].&",
"#(2,Rating)": "[&].rating-primary",
"Arr1": {
"*": {
"*": "[&3].&"
}
}
}
}
}
}
]
in this case, no need to write all elements individually except for Rating
Related
I have this data:
{
"data": [
{
"a": 11,
"b": 12,
"c": 13
},
{
"a": 21,
"b": 22,
"c": 23
},
{
"a": 31,
"b": 32,
"c": 33
}
]
}
I want to collate it into arrays based on the key names like this:
{
"data": [
{
"a": [
11,
21,
31
]
},
{
"b": [
12,
22,
32
]
},
{
"c": [
13,
23,
33
]
}
]
}
For this example we can assume that each element of the original array has the same set of keys, just different values for them.
This jq code can do it for select keys:
jq '(.data[0]|keys_unsorted) as $keys | { ($keys[2]): [.data[]|.[$keys[2]]] }' data.json
{
"c": [
13,
23,
33
]
}
How do I do this for all keys?
One way would be using to_entries to access the keys, then group_by for the grouping, and map to construct the final structure:
.data |= (map(to_entries[]) | group_by(.key) | map({(first.key): map(.value)}))
{
"data": [
{
"a": [
11,
21,
31
]
},
{
"b": [
12,
22,
32
]
},
{
"c": [
13,
23,
33
]
}
]
}
Suggestion: Instead of having an array of objects with a single field, you could instead build one object containing all fields:
.data |= reduce (.[] | to_entries[]) as {$key, $value} ({}; .[$key] += [$value])
{
"data": {
"a": [
11,
21,
31
],
"b": [
12,
22,
32
],
"c": [
13,
23,
33
]
}
}
I have this document with this structure
{
"itemA": {
"subItemA": {
"20200104": [
{
"item": 1,
"item2": 2,
"item3": 3
},
{
"item": 4,
"item2": 5,
"item3": 6
},
{
"item": 7,
"item2": 8,
"item3": 9
}
]
}
}
}
Let's say I know the key of itemA, subItemA, and 20200104 and i want to retrieve the data so it will look like this:
[
{
"item": 1,
"item2": 2,
"item3": 3
},
{
"item": 4,
"item2": 5,
"item3": 6
},
{
"item": 7,
"item2": 8,
"item3": 9
}
]
This may be possible if I use JavaScript function (like db.data.find().forEach(function () ....) but is it possible to only use the "native query"?
First $unwind the array then use $replaceRoot
db.collection.aggregate([
{
$unwind: "$itemA.subItemA.20200104"
},
{
$replaceRoot: {
newRoot: "$itemA.subItemA.20200104"
}
}
])
I have 2 Collections/ Result sets the first one is products and second one is sizes
"products": [
{
"category_id": 5,
"id": 5,
"code": "A",
"name": "Pizzabrot",
"description": "",
"product_prices": [
{
"product_id": 5,
"price": 2.5,
"size_id": 15
},
{
"product_id": 5,
"price": 3.5,
"size_id": 16
}
]
},
{
"category_id": 5,
"id": 6,
"code": "B",
"name": "Pizzabrot mit Knoblauch",
"description": "",
"product_prices": [
{
"product_id": 6,
"price": 3,
"size_id": 15
},
{
"product_id": 6,
"price": 4,
"size_id": 16
}
]
}]
AND
"sizes": [
{
"id": 15,
"name": "Klein",
"category_id": 5
},
{
"id": 16,
"name": "Gro\u00df",
"category_id": 5
}
]
I want to replace every product_prices.size_id with it's name from sizes Collection
From what I can see it would probably be better to associate ProductPrices and Sizes and fetch them with your Products.
If this does not fit your needs for some reason, you could find the sizes by using a find('list') (see Docs) like this:
$query = $this->Sizes->find('list', [
'keyField' => 'id',
'valueField' => 'name'
]);
$sizes = $query->toArray();
Then loop through the products and its product_prices and do something like.
$product_price->size_id = $sizes[$product_price->size_id];
Please prefer the first solution. ;-)
I'm trying to write a function that iterates through an array of objects, and pushes in new ones (ones that have a unique name), while updating ones that have already been seen.
Say for example, I have this array:
$scope.myArray = [
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
];
By the end of the function, the new array should be:
$scope.myNewArray = [
{ "name": "Apple", "total": 32, "applicable": 42},
{ "name": "Cherry", "total": 24, "applicable": 54},
{ "name": "Plum", "total": 28, "applicable": 42},
{ "name": "Banana", "total": 14, "applicable": 21 },
];
Here's what I have so far:
$scope.myArray = [
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
];
$scope.myNewArray = [];
$scope.myArray.filter( function () {
var i = $scope.myNewArray.indexOf($scope.myArray.name);
if ($scope.myNewArray.indexOf($scope.myArray.name) < 0)
$scope.myNewArray.push($scope.myArray);
else {
$scope.myNewArray[i].total += $scope.myArray.total;
$scope.myNewArray[i].applicable += $scope.myArray.applicable;
}
});
The problem I'm encountering is everything gets pushed into the new array. That and I believe my else statement where I'm adding the values to the previous record might be wrong.
Also, hard-coding an array for each name doesn't work as this is just a simple example with a small dataset.
Can anyone lend a hand?
try this approach:
create object where keys are name property and total along with applicable are already calculated (Array.prototype.reduce)
Iterate over keys of previously created object and transform it back to array (Object.keys and Array.prototype.map)
var res = {};
res = Object.keys([
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
].reduce(function (res, item) {
if (res[item.name]) {
res[item.name].total += item.total;
res[item.name].applicable += item.applicable;
}
else {
res[item.name] = item;
}
return res;
}, res)).map(function(key) {
return res[key];
});
console.log(res);
adding less hardcoded solution:
var myArray = [
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Apple", "total": 16, "applicable": 21 },
{ "name": "Cherry", "total": 12, "applicable": 27 },
{ "name": "Plum", "total": 14, "applicable": 21 },
{ "name": "Banana", "total": 14, "applicable": 21 },
];
var res = {};
// add keys for loopable integers which will be summed
var loopables = Object.keys(myArray[0]).filter(function (key) {
return Number.isInteger(myArray[0][key]);
});
res = Object.keys(myArray.reduce(function (res, item) {
if (res[item.name]) {
loopables.forEach(function (loopableKey) {
res[item.name][loopableKey] += item[loopableKey];
});
}
else {
res[item.name] = item;
}
return res;
}, res)).map(function(key) {
return res[key];
});
console.log(res);
here I only rely on the main key name, the rest integer properties are automatically summed, by iterating over loopables keys array, calculated at the beginning
plunker with Angular: https://plnkr.co/edit/MRr2QRULG8TYs2CqA1By?p=preview
What I do to simply such things is I use angular forEach and in your case, I'd make three different arrays and fill them up with this information. Then I'd use indexof on them and push them to myNewArray. Its easier to work on simple arrays than array objects.
For example on forEach Angular forEach.
I think you can do it in pure javascript
check this following snippet
var obj = [{
"name": "Apple",
"total": 16,
"applicable": 21
}, {
"name": "Cherry",
"total": 12,
"applicable": 27
}, {
"name": "Plum",
"total": 14,
"applicable": 21
}, {
"name": "Apple",
"total": 16,
"applicable": 21
}, {
"name": "Cherry",
"total": 12,
"applicable": 27
}, {
"name": "Plum",
"total": 14,
"applicable": 21
}, {
"name": "Banana",
"total": 14,
"applicable": 21
}, ];
var newObj = [];
MergeObjectProperties(obj);
function MergeObjectProperties(obj) {
Object.keys(obj).forEach(function (key) {
var name = obj[key].name;
var exists = checkProperty(name, newObj)
if (newObj.length == 0 || !exists)
newObj.push(obj[key]);
else {
newObj[exists]["total"] = obj[key].total + newObj[exists]["total"];
newObj[exists]["applicable"] = obj[key].applicable + newObj[exists]["applicable"];
}
});
console.log(newObj);
}
function checkProperty(prop, newObj) {
var result;
Object.keys(newObj).forEach(function (key) {
if (newObj[key]["name"] === prop) {
result = key
}
});
return result;
}
Hope this helps
I have a prop containing an array of integers:
_source: {
counts: [
11,
7,
18,
3,
22
]
}
From another post I know that I can filter by a range using:
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"range": {
"counts": {
"gte": 10,
"lte": 20
}
}
}
}
}
}
However, I need to additionally know if the range match count is greater than a certain number. For instance, I only want records back which have less than 3 counts matching between 10 and 20.
Mapping used:
{
"properties" : {
"counts" : {
"type" : "integer"
}
}
}
These are the docs I indexed:
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "test_index",
"_type": "test",
"_id": "2",
"_score": 1,
"_source": {
"counts": [
13,
17
]
}
},
{
"_index": "test_index",
"_type": "test",
"_id": "1",
"_score": 1,
"_source": {
"counts": [
11,
7,
18,
3,
22
]
}
},
{
"_index": "test_index",
"_type": "test",
"_id": "3",
"_score": 1,
"_source": {
"counts": [
11,
19
]
}
}
]
}
}
Now try this query:
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": [
{"script" : { "script" : "doc['counts'].values.size() < 4" }},
{"range": { "counts": { "gte": 10, "lte": 20 } }}
]
}
}
}
Results: Only doc id 2 and 3 are returned. Doc 1 is not returned.
{
"took": 29,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "test_index",
"_type": "test",
"_id": "2",
"_score": 1,
"_source": {
"counts": [
13,
17
]
}
},
{
"_index": "test_index",
"_type": "test",
"_id": "3",
"_score": 1,
"_source": {
"counts": [
11,
19
]
}
}
]
}
}
Is this what you are trying to do?