Json Path sum functions for List of List - javascript

I am using JSON path to do something similar to this:
I have copied the JSON path example, but modified the price field to represent price Year-over-Year (number to array).
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": [ 1, 2, 3 ]
},
{
"category" :"fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": [ 1, 2, 3 ]
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": [ 1, 2, 3 ]
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": [ 1, 2, 3 ]
}
],
"bicycle": {
"color": "red",
"price": [ 1, 2, 3 ]
}
},
"expensive": 10
}
What I want to find is the year over year total price for all the books.
I can get a Array of Array (lets say res) using: $.store.book[*].price
Output:
[
[ 1, 2, 3 ],
[ 1, 2, 3 ],
[ 1, 2, 3 ],
[ 1, 2, 3 ]
]
I want to further reduce this output (by sum) to:
[4, 8, 12] // Sum up nth element of each array.
// (res[0][0] + res[1][0] + res[2][0] + res[3][0] = 4 ... and so on)
Is there a way to achieve this using jsonpath (preferred)/any other JavaScript syntax ?

let data = [
[ 1, 7, 3 ],
[ 2, 6, 3 ],
[ 3, 5, 3 ],
[ 4, 4, 3 ]
]
let result = data[0].map((_, colIndex) => data.map(row => row[colIndex]))
.map(value => value.reduce((acc, value) => acc + value, 0))
console.log(result) // [ 10, 22, 12 ]
The first map transposes rows and columns, the second map sums up what after the transpose are the rows.

const data = {
"store": {
"book": [{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": [1, 2, 3]
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": [1, 2, 3]
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": [1, 2, 3]
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": [1, 2, 3]
}
]
}
}
const prices = []
data.store.book.forEach(book => {
book.price.forEach((price, index) => {
if (!prices[index]) prices[index] = 0;
prices[index] += price;
})
})
console.log(prices)

You can sum each of the columns in the matrix by mapping the first row columns to a reduction of each of their subsequent rows.
const data = {"store":{"book":[{"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":[1,2,3]},{"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":[1,2,3]},{"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":[1,2,3]},{"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":[1,2,3]}],"bicycle":{"color":"red","price":[1,2,3]}},"expensive":10};
const sumColumns = (matrix) =>
matrix[0].map((_, col) =>
matrix.reduce((acc, data, row) => acc + data[col], 0));
const bookPrices = data.store.book.map(({ price }) => price);
const priceSums = sumColumns(bookPrices);
console.log(priceSums); // [ 4, 8, 12 ]
.as-console-wrapper { top: 0; max-height: 100% !important; }

You can use some .map() and Array.prototype.reduce() paired with comma operator.
const a = { "store": { "book": [{ "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": [1, 2, 3] }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": [1, 2, 3] }, { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": [1, 2, 3] }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": [1, 2, 3] } ], "bicycle": { "color": "red", "price": [1, 2, 3] } }, "expensive": 10 }.store.book
console.log(a.map(x=>x.price).reduce((x,y)=>(y.map((i,z)=>x[z]+=y[z]),x)))

Related

How to push object value from first array to second array by id?

I have an 2 array, first array:
[
{
"id": 1,
"name": "jumlah_sd",
"title": "Jumlah SD",
"value": "1222"
},
{
"id": 2,
"name": "jumlah_smp",
"title": "Jumlah SMP",
"value": "1322"
}
]
and second array with no value:
[
{
"id": 1,
"name": "jumlah_sd",
"title": "Jumlah SD"
},
{
"id": 2,
"name": "jumlah_smp",
"title": "Jumlah SMP"
},
{
"id": 3,
"name": "jumlah_sma",
"title": "Jumlah SMA"
}
]
My question is, how to push by their "id" object value in first array to second array in JavaScript? And if there is no value in other object, so the value become "value": "" in second array.
One way to do is to create an object with the id as the key and value as value for easy lookup.
After that map through the second array and add the id using the lookup object.
const arr1 = [ { "id": 1, "name": "jumlah_sd", "title": "Jumlah SD", "value": "1222" }, { "id": 2, "name": "jumlah_smp", "title": "Jumlah SMP", "value": "1322" }]
let arr2 = [ { "id": 1, "name": "jumlah_sd", "title": "Jumlah SD" }, { "id": 2, "name": "jumlah_smp", "title": "Jumlah SMP" },{ "id": 3, "name": "jumlah_sma", "title": "Jumlah SMA" }]
const idLookup = arr1.reduce((acc,{id,value}) => {
acc[id]??=value
return acc
},{})
arr2 = arr2.map((x) => ({...x, value: idLookup[x.id]||""}))
console.log(arr2)
Another approach is to use find inside map but it will intuitively be slower since the find is called in each iteration inside the map
const arr1 = [ { "id": 1, "name": "jumlah_sd","title": "Jumlah SD", "value": "1222" }, { "id": 2,"name": "jumlah_smp", "title": "Jumlah SMP", "value": "1322" }]
let arr2 = [ { "id": 1, "name": "jumlah_sd", "title": "Jumlah SD" }, { "id": 2, "name": "jumlah_smp", "title": "Jumlah SMP" }, { "id": 3, "name": "jumlah_sma", "title": "Jumlah SMA" }]
arr2 = arr2.map((x) => {
const entry = arr1.find(({id}) => x.id === id)
return {...x,value:entry?.value||''}
})
console.log(arr2)

Return children tree from array

I have an array of regions the highest region has key: 10 and parent_id: null and I want to restructure this array to return a tree.
Regions tree should look like if the input is [10]
Egypt
Zone 1
Tagamo3
Giza
Helwan
Fayoum
Zone 2
Gesr ElSuis
test
Delta
Mohandeseen
Down Town
Array:
[
{
"key": 1,
"title": "Zone 1",
"parent_id": 10
},
{
"key": 2,
"title": "Zone 2",
"parent_id": 10
},
{
"key": 3,
"title": "Tagamo3",
"parent_id": 1
},
{
"key": 4,
"title": "Gesr ElSuis",
"parent_id": 2
},
{
"key": 5,
"title": "Delta",
"parent_id": 2
},
{
"key": 6,
"title": "Mohandeseen",
"parent_id": 2
},
{
"key": 7,
"title": "Giza",
"parent_id": 1
},
{
"key": 8,
"title": "Helwan",
"parent_id": 1
},
{
"key": 9,
"title": "Down Town",
"parent_id": 2
},
{
"key": 10,
"title": "Egypt",
"parent_id": null
},
{
"key": 11,
"title": "Fayoum",
"parent_id": 1
},
{
"key": 12,
"title": "test",
"parent_id": 4
}
]
The output I want to achieve if input is [10]:
[
{
"key": 10,
"title": "Egypt",
"parent_id": null,
"children": [
{
"key": 1,
"title": "Zone 1",
"parent_id": 10,
"children": [
{
"key": 3,
"title": "Tagamo3",
"parent_id": 1,
"children": []
},
{
"key": 7,
"title": "Giza",
"parent_id": 1,
"children": []
},
{
"key": 8,
"title": "Helwan",
"parent_id": 1,
"children": []
},
{
"key": 11,
"title": "Fayoum",
"parent_id": 1,
"children": []
}
]
},
{
"key": 2,
"title": "Zone 2",
"parent_id": 10,
"children": [
{
"key": 4,
"title": "Gesr ElSuis",
"parent_id": 2,
"children": [
{
"key": 12,
"title": "test",
"parent_id": 4,
"children": []
}
]
},
{
"key": 5,
"title": "Delta",
"parent_id": 2,
"children": []
},
{
"key": 6,
"title": "Mohandeseen",
"parent_id": 2,
"children": []
},
{
"key": 9,
"title": "Down Town",
"parent_id": 2,
"children": []
}
]
}
]
}
]
Regions tree should look like if the input is [1,2]
Zone 1
Tagamo3
Giza
Helwan
Fayoum
Zone 2
Gesr ElSuis
test
Delta
Mohandeseen
Down Town
Regions tree should look like if the input is [1]
Zone 1
Tagamo3
Giza
Helwan
Fayoum
May not be the most optimized, but I gave it a try:
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
const buildTree = key => arr.filter(x => x.parent_id === key)
.map(x => ({ ...x, children: buildTree(x.key) }));
console.log(buildTree(null));
To build multiple trees, this could work:
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
const buildNode = x => ({...x, children: buildTree(x.key)});
const buildTree = key => arr.filter(x => x.parent_id === key)
.map(buildNode);
const buildTrees = keys => arr.filter(x => keys.includes(x.key))
.map(buildNode);
console.log(buildTrees([1, 2]));
based on jcalz snippet, but requires only a single pass over the input array.
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
/*const lookup: Record<number|"roots", Tree[]> = { roots: [] };*/
const lookup = { roots: [] };
for (const item of arr) {
// get or create the "children" array for my parent.
// parent may not be known yet, but I'm already collecting its children, my siblings.
const siblings = lookup[item.parent_id ?? "roots"] ??= [];
// add myself to that children array.
siblings.push({
...item,
// get or create my children array.
children: lookup[item.key] ??= []
});
}
// it's up to you to handle `lookup.roots.length !== 1`
console.log(lookup.roots);
.as-console-wrapper{top:0;max-height:100%!important}

Group nested objects with reduce

I have an array with objects
const data = [{
"id": 19887003,
"category": "Shops",
"details": "Shoe Store",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 19234003,
"category": "Shops",
"details": "Shoe Point",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 190456003,
"category": "Food",
"details": "Korean",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 190111003,
"category": "Food",
"details": "Chinese",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 1902303,
"category": "Food",
"details": "Lounge",
"star": 4,
"subCategory": "Bar",
}]
this is a small piece but the structure is the same for all objects: i have a category, with multiple subcategories and sometimes the subcategory has details..for example the category food has the subcategory restourant and restourant has many types (chinese, korean).
My goal is to get a structure like that:
[
{
"category": "Food",
"subCategories": [
{
"Subcategory": "Bar",
"details": [
{
name: "Lounge",
star: 2,
id: 1902303
}
]
},
{
"Subcategory": "Restaurant",
"details": [
{
name: "Chinese",
star: 4,
id: 190111003
},
{
name: "Korean",
star: 4,
id: 190456003
}
]
}
},
{
"category": "Shops",
"subCategories": [
{
"Subcategory": "Outlet",
"details": [
{
name: "Shoe Store",
star: 2,
id: 19887003
},
{
name: "Shoe Point",
star: 2,
id: 19234003
}
]
}
]
}
]
My attempt:
const groupedCategories = data.reduce((accumulator, element) => {
const detail = element.details;
const category = element.category;
const subCategory = element.subCategory;
if (accumulator[category]){
return {
...accumulator,
[category]: {
...accumulator[category],
subCategories: [...new Set([...accumulator[category].subCategories,subCategory])],
}
}}
else {
return {
...accumulator,
[category]: {
subCategories: [subCategory],
}
}
}
}, {});
I tried use reduce method like that but this is not the exact structure I desire in particular how to put details fields into subcategories.
Thanks
array.reduce seems to be the right choice. Simplest approach is to have double if statement to check if previous element (category and subcategory exists) and either push into existing array or create new object on upper level:
const data = [{
"id": 19887003,
"category": "Shops",
"details": "Shoe Store",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 19234003,
"category": "Shops",
"details": "Shoe Point",
"star": 2,
"subCategory": "Outlet",
},
{
"id": 190456003,
"category": "Food",
"details": "Korean",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 190111003,
"category": "Food",
"details": "Chinese",
"star": 4,
"subCategory": "Restaurant",
},
{
"id": 1902303,
"category": "Food",
"details": "Lounge",
"star": 4,
"subCategory": "Bar",
}]
let output = data.reduce((acc,cur) => {
let {category, subCategory, ...rest} = cur;
let prevCat = acc.find(x => x.category === category);
if(!prevCat){
acc.push({category, subCategories: [{subCategory, details: [rest]}]});
} else {
let prevSubCat = prevCat.subCategories.find(x => x.subCategory === subCategory);
if(!prevSubCat) {
prevCat.subCategories.push({subCategory, details: [rest]});
} else {
prevSubCat.details.push(rest);
}
}
return acc;
}, []);
console.log(output);

Comparing two JSONs in NodeJs & Express

I'm trying to compare two JSONs files in NodeJs.
If there is a review that matches the place id, I need to push the review data into the places JSON. If there are no matching reviews, it pushes an empty array.
Here is the code:
//places JSON
[{
"id": 1,
"title": "Hotel in Sidney",
"maxNumberOfGuests": 5,
"description": "Quiet place by the water.",
"createdAt": "2019/12/7 14:34",
"price": 120
},
{
"id": 2,
"title": "Cabin in Italy",
"maxNumberOfGuests": 2,
"description": "Romantic lake cabin for two.",
"createdAt": "2019/4/7 10:00",
"price": 250
}
]
//reviews JSON
[{
"id": 1,
"numberOfStars": 3,
"content": "Comfy place",
"placeId": 1,
"createdAt": 12345
},
{
"id": 2,
"numberOfStars": 4,
"content": "Awesome lake view.",
"placeId": "",
"createdAt": 23456
}
]
Here is the desired result:
[{
"id": 1,
"title": "Hotel in Sidney",
"maxNumberOfGuests": 5,
"description": "Quiet place by the water.",
"createdAt": "2019/12/7 14:34",
"reviews": [{
"id": 1,
"numberOfStars": 3,
"content": "Comfy place",
"placeId": 1,
"createdAt": 12345
}],
"price": 120
},
{
"id": 2,
"title": "Cabin in Italy",
"maxNumberOfGuests": 2,
"description": "Romantic lake cabin for two.",
"createdAt": "2019/4/7 10:00",
"reviews": [],
"price": 250
}
]
this is how far I could get:
places.forEach(p => {
const { id } = p;
console.log(id);
return id;
});
reviews.forEach(r => {
const { id, numberOfStars, content, placeId, createdAt } = r;
// console.log(id, numberOfStars, content, placeId, createdAt);
console.log(r);
return r;
});
//node express routes to places where will display the desired result.
router.get('/places', function(req, res) {
res.json(places);
});
I just can't make it work and need some help.
Thanks in advance.
try this
let places =[{
"id": 1,
"title": "Hotel in Sidney",
"maxNumberOfGuests": 5,
"description": "Quiet place by the water.",
"createdAt": "2019/12/7 14:34",
"price": 120
},
{
"id": 2,
"title": "Cabin in Italy",
"maxNumberOfGuests": 2,
"description": "Romantic lake cabin for two.",
"createdAt": "2019/4/7 10:00",
"price": 250
}
];
let reviews =[{
"id": 1,
"numberOfStars": 3,
"content": "Comfy place",
"placeId": 1,
"createdAt": 12345
},
{
"id": 2,
"numberOfStars": 4,
"content": "Awesome lake view.",
"placeId": "",
"createdAt": 23456
}
];
places.forEach(function(place) {
place.reviews = reviews.filter(review => review.placeId ===place.id);
});
console.log(places);
I would first reduce the reviews array into an object keyed by placeId.
const reviews = [{
"id": 1,
"numberOfStars": 3,
"content": "Comfy place",
"placeId": 1,
"createdAt": 12345
},
{
"id": 2,
"numberOfStars": 4,
"content": "Awesome lake view.",
"placeId": "",
"createdAt": 23456
}
];
const reviewsHashmap = reviews.reduce((acc, review) => {
if (!review.placeId) return acc;
acc[review.placeId] = acc[review.placeId] || [];
acc[review.placeId].push(review);
return acc;
}, {})
This makes it more performant when adding the reviews property, because now you do not need to filter the reviews array again and again for each place.
const places = [
{
"id": 1,
"title": "Hotel in Sidney",
"maxNumberOfGuests": 5,
"description": "Quiet place by the water.",
"createdAt": "2019/12/7 14:34",
"price": 120
},
{
"id": 2,
"title": "Cabin in Italy",
"maxNumberOfGuests": 2,
"description": "Romantic lake cabin for two.",
"createdAt": "2019/4/7 10:00",
"price": 250
}
];
const placesWithReviews = places.map(place => ({
...place,
reviews: reviewsHashmap[place.id] || []
}))
Now you should have the original places array, but with each place an additional reviews property.

finding a value belongs to array or not in javascript

I have following JavaScript Array
business: [{
"id": 22,
"name": "Private",
"max_mem": 0,
"gen_roomtype_id": 4,
"status": 1,
"type": 0,
"set_value": 1
},
{
"id": 6,
"name": "Standard ward",
"max_mem": 0,
"gen_roomtype_id": 2,
"status": 1,
"type": 0,
"set_value": 1
},
{
"id": 7,
"name": "Semi Private",
"max_mem": 0,
"gen_roomtype_id": 3,
"status": 1,
"type": 0,
"set_value": 1
}],
"gen": [{
"id": 5,
"name": "laboratory",
"description": "",
"sharing": 0,
"set_value": 2
}],
And i have a idArray as following
idArray: [5, 7]
i would like to know whether the idArray values are belongs to "gen" Array or
"business" Array.
You can use the function every
This approach assumes the input data is an object.
var obj = { business: [{ "id": 5, "name": "Private", "max_mem": 0, "gen_roomtype_id": 4, "status": 1, "type": 0, "set_value": 1 }, { "id": 6, "name": "Standard ward", "max_mem": 0, "gen_roomtype_id": 2, "status": 1, "type": 0, "set_value": 1 }, { "id": 7, "name": "Semi Private", "max_mem": 0, "gen_roomtype_id": 3, "status": 1, "type": 0, "set_value": 1 } ], "gen": [{ "id": 5, "name": "laboratory", "description": "", "sharing": 0, "set_value": 2 }]
};
var idArray = [5, 7];
var resultBusiness = idArray.every(n => obj.business.some(b => b.id === n));
var resultGen = idArray.every(n => obj.gen.some(b => b.id === n));
console.log("All in business: ", resultBusiness);
console.log("All in Gen: ", resultGen);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories