How to reconstruct array data? - arrays

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
]
}
}

Related

Jolt Transform complex json to a flat json

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

Query Nested Object and Get the Desired Key in MongoDB

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"
}
}
])

Loop within a Loop when condition satisfies should return newsingleArray

I have two loops, when the condition is satisfied I want to return single array.
My configuration array is as follows
"configuration": [
{
"position": "O",
"side": "L",
"type": 21,
"wheel": 1,
"wheels": 20
},
{
"position": "I",
"side": "L",
"type": 21,
"wheel": 2,
"wheels": 20
},
]
My sensor array is as follows
“Sensor”: [
{
"pressure": 8126,
"sub_item": "1",
"temp": 16,
"time": 1572243074,
},
{
"pressure": 8205,
"sub_item": "10",
"temp": 18.3,
"time": 1572243092,
},
]
I am looping through sensor array from configuration array and making a condition here
let finalarray = []
configuration.forEach((e1) => sensorData.forEach((e2) => {
if (e1.wheel == e2.sub_item) {
finalarray.push(e1)
finalarray.push(e2)
console.log(JSON.stringify(finalarray))
}
}
))
I am expecting final array should be single array with both configuration and sensor array but I am receiving two different arrays.
I am guessing you wish to merge your object values instead of pushing them separately in the array for which you can use the spread operator syntax
const configuration = [
{
"position": "O",
"side": "L",
"type": 21,
"wheel": 1,
"wheels": 20
},
{
"position": "I",
"side": "L",
"type": 21,
"wheel": 2,
"wheels": 20
},
]
const sensorData = [
{
"pressure": 8126,
"sub_item": "1",
"temp": 16,
"time": 1572243074,
},
{
"pressure": 8205,
"sub_item": "10",
"temp": 18.3,
"time": 1572243092,
},
]
let finalarray = []
configuration.forEach((e1) => sensorData.forEach((e2) => {
if (e1.wheel == e2.sub_item) {
finalarray.push({...e1, ...e2})
}
}))
console.log(finalarray)

jq: Turn json object values into arrays

I have got the following array of objects (this is just an excerpt, also the objects are bigger):
[{
"DATE": "10.10.2017 01:00",
"ID": "X",
"VALUE_ONE": 20,
"VALUE_TWO": 5
},
{
"DATE": "10.10.2017 02:00",
"ID": "X",
"VALUE_ONE": 30,
"VALUE_TWO": 7
},
{
"DATE": "10.10.2017 03:00",
"ID": "X",
"VALUE_ONE": 25,
"VALUE_TWO": 2
},
{
"DATE": "10.10.2017 01:00",
"ID": "Y",
"VALUE_ONE": 10,
"VALUE_TWO": 9
},
{
"DATE": "10.10.2017 02:00",
"ID": "Y",
"VALUE_ONE": 20,
"VALUE_TWO": 5
},
{
"DATE": "10.10.2017 03:00",
"ID": "Y",
"VALUE_ONE": 50,
"VALUE_TWO": 5
},
{
"DATE": "10.10.2017 01:00",
"ID": "Z",
"VALUE_ONE": 55,
"VALUE_TWO": 3
},
{
"DATE": "10.10.2017 02:00",
"ID": "Z",
"VALUE_ONE": 60,
"VALUE_TWO": 7
},
{
"DATE": "10.10.2017 03:00",
"ID": "Z",
"VALUE_ONE": 15,
"VALUE_TWO": 7
}
]
To simplify this for a web application, and also to reduce file size, I would like to convert the "VALUE_ONE","VALUE_TWO" and "DATE" values to arrays for each "ID" just like this:
[{
"DATE": ["10.10.2017 01:00", "10.10.2017 02:00", "10.10.2017 03:00"],
"ID": "X",
"VALUE_ONE": [20, 30, 25],
"VALUE_TWO": [5, 7, 2]
},
{
"DATE": ["10.10.2017 01:00", "10.10.2017 02:00", "10.10.2017 03:00"],
"ID": "Y",
"VALUE_ONE": [10, 20, 50],
"VALUE_TWO": [9, 5, 5]
},
{
"DATE": ["10.10.2017 01:00", "10.10.2017 02:00", "10.10.2017 03:00"],
"ID": "Z",
"VALUE_ONE": [55, 60, 15],
"VALUE_TWO": [3, 7, 7]
}
]
Here it is important that you need to be able find the values that are linked to a certain time (date). As the input values for "DATE" are consecutive, you most probably do not need the DATE value anymore to find the requested "VALUE.." value. You can probably just use the index of the array for that (index=0 is always 10.10.2017 01:00, index=1 is ... 02:00 etc.).
Is it possible to do it like that? This would keep the file size even smaller.
Thanks!
With 2-step reduce(it doesn't look beautiful but works):
jq 'reduce group_by(.ID)[] as $a ([]; . + [ reduce $a[] as $o
({"DATE":[],"VALUE_ONE":[],"VALUE_TWO":[]};
.DATE |= .+ [$o.DATE] | .ID = $o.ID |.VALUE_ONE |= .+ [$o.VALUE_ONE]
| .VALUE_TWO |= .+ [$o.VALUE_TWO]) ] )' input.json
The output:
[
{
"DATE": [
"10.10.2017 01:00",
"10.10.2017 02:00",
"10.10.2017 03:00"
],
"VALUE_ONE": [
20,
30,
25
],
"VALUE_TWO": [
5,
7,
2
],
"ID": "X"
},
{
"DATE": [
"10.10.2017 01:00",
"10.10.2017 02:00",
"10.10.2017 03:00"
],
"VALUE_ONE": [
10,
20,
50
],
"VALUE_TWO": [
9,
5,
5
],
"ID": "Y"
},
{
"DATE": [
"10.10.2017 01:00",
"10.10.2017 02:00",
"10.10.2017 03:00"
],
"VALUE_ONE": [
55,
60,
15
],
"VALUE_TWO": [
3,
7,
7
],
"ID": "Z"
}
]
The following solution avoids group_by for two reasons:
efficiency
the sort used by group_by in jq version 1.5 might not be stable, which complicates things.
Instead we use bucketize defined as follows:
def bucketize(f): reduce .[] as $x ({}; .[$x|f] += [$x] );
To keep things simple, we will also define the following helper function:
# compactify an array with a single ID
def compact:
. as $in
| reduce (.[0]|keys_unsorted[]) as $key ({};
. + {($key): $in|map(.[$key])})
+ {"ID": .[0].ID}
;
Solution
[bucketize(.ID)[] | compact]
This will ensure things are OK even if the set of dates differ across IDs, and even if the JSON objects are not grouped initially by date.
(If you want to drop "DATE" altogether in the final results, then replace the call to compact by compact | del(.DATE) in the line above.)
Output
[
{
"DATE": [
"10.10.2017 01:00",
"10.10.2017 02:00",
"10.10.2017 03:00"
],
"ID": "X",
"VALUE_ONE": [
20,
30,
25
],
"VALUE_TWO": [
5,
7,
2
]
},
{
"DATE": [
"10.10.2017 01:00",
"10.10.2017 02:00",
"10.10.2017 03:00"
],
"ID": "Y",
"VALUE_ONE": [
10,
20,
50
],
"VALUE_TWO": [
9,
5,
5
]
},
{
"DATE": [
"10.10.2017 01:00",
"10.10.2017 02:00",
"10.10.2017 03:00"
],
"ID": "Z",
"VALUE_ONE": [
55,
60,
15
],
"VALUE_TWO": [
3,
7,
7
]
}
]
Here is a solution using reduce, setpath, getpath, del and symbolic variable destructuring. It will collect all the values for keys other than ID and DATE (eliminating the need to hardcode VALUE_ONE, etc.) in parallel arrays.
reduce (.[] | [.ID, .DATE, del(.ID,.DATE)]) as [$id,$date,$v] ({};
(getpath([$id, "DATE"])|length) as $idx
| setpath([$id, "ID"]; $id)
| setpath([$id, "DATE", $idx]; $date)
| reduce ($v|keys[]) as $k (.; setpath([$id, $k, $idx]; $v[$k]))
)
| map(.)
Try it online!
If your data set is small enough, you could just group them up by id and map to the desired results. It won't be super efficient compared to a streaming solution, but will be the simplest to implement using builtins.
group_by(.ID) | map({
DATE: map(.DATE),
ID: .[0].ID,
VALUE_ONE: map(.VALUE_ONE),
VALUE_TWO: map(.VALUE_TWO)
})

AngularJS: How to check if value exists in array of objects?

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

Resources