Get parent object using jsonpath query - javascript

i have the json below:
{
"data": [
{
"name": "product1",
"details": ["lorem ipsum", [
{
"code": "prd1"
},
{
"code": "prd11"
}]
]
},
{
"name": "product2",
"details": ["lorem ipsum", [
{
"code": "prd2"
},
{
"code": "prd22"
}]
]
}
]
}
and i want to retrieve the name of the product based on the code, so
i wrote this query
$.data..[?(#.code=="prd1")]
Result:
[
{
"code": "prd1"
}]
Expected result:
[{name: "product1"}]

You should move up the tree until you reach the value of a specific key. At first, we need to reach the first named ancestor, which is details, then - to get its first parent, which holds the needed name property. This can be done using ancestor() function and parent() function:
$.data..[?(#.code=="prd1")].ansector("details").parent()['name'];

Related

Insert key's value in the array of object having same key

Say I've an array of object:
const Info = [{
"id": "1aa2",
"details": [{
"name": "rusty",
"age": "12",
"favourite": [ObjectId("602b696cb783fc15845d015e"), ObjectId("602b696cb783fc15845d0112")]
}]
},
{
"id": "3aa2",
"details": [{
"name": "john",
"age": "122",
"favourite": [ObjectId("602b696cb783fc15845d0112s"), ObjectId("602b696cb783fc15845d01wqs")]
}]
}
]
I want to merge favourite in one array as:
["favourite": [
ObjectId("602b696cb783fc15845d015e"),
ObjectId("602b696cb783fc15845d0112"),
ObjectId("602b696cb783fc15845d0112s"),
ObjectId("602b696cb783fc15845d01wqs")
]
]
I tried using for loop but it's creating nasty nested for loop which is reducing performance a lot.
Basically, you need to iterate through Info collection, iterate through 'details' sub-collection, and copy all data into a new array. After, just create a new structure using favs variable content or paste this code as object value directly.
BTW your result array need's to contain an object at least, like that:
[ { favourite: [...] } ]
About nested structures, you should try https://lodash.com/docs/4.17.15#flatMapDeep (at least just check the code)
const Info = [{
"id": "1aa2",
"details": [{
"name": "rusty",
"age": "12",
"favourite": ['ObjectId("602b696cb783fc15845d015e")', 'ObjectId("602b696cb783fc15845d0112")']
}]
},
{
"id": "3aa2",
"details": [{
"name": "john",
"age": "122",
"favourite": ['ObjectId("602b696cb783fc15845d0112s")', 'ObjectId("602b696cb783fc15845d01wqs")']
}]
}
]
const favs = Info.reduce((acc, item) => {
item.details.forEach(detail => {
acc.push(...detail.favourite);
})
return acc;
}, []);
console.log(favs);

Replacing Data in Array

I am working on an angular application. I have an array as follows:
[{
"Name": "Andy"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda"
}]
I have another array which containes data as follows:
[
{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Billy",
"Id": "2",
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Ciena",
"Id": 5
"Time": "2020-06-19T11:05+00:00"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}
]
I want a resultant array such that code should check if Name is present in first array, then it should copy data from second array for that Name and push it in resultant array. For example common name between above two array is Andy and Doda, so data from Andy and Doda should be pushed to resultant array as follows:
[{
"Name": "Andy",
"Id": "1",
"Time": "2020-06-19T11:02+00:00"
},
{
"Name": "Bayer"
},
{
"Name": "James"
},
{
"Name": "Doda",
"Id": "4",
"Time": "2020-06-19T11:05+00:00"
}]
At run time I may get many names so code should be generic. I was trying following code which I got over stackoverflow itself
this.newArray = _.map(this.resultantArray, item => {
const value = _.find(this.dataArray, ['Name', item]);
const obj = value ? value : {Name: item};
return obj;
});
But this code is not working as expected as it works fine for the first time but when data comes for second time it appends data to previous data. I want array to be populated again freshly every time I send data. Please help
You can do this with vanilla JS no need for lodash. You can first map it and inside that you can find the value from second array otherwise return the current object:
var arrayTwo = [ { "Name": "Andy", "Id": "1", "Time": "2020-06-19T11:02+00:00" }, { "Name": "Billy", "Id": "2", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Ciena", "Id": "5", "Time": "2020-06-19T11:05+00:00" }, { "Name": "Doda", "Id": "4", "Time": "2020-06-19T11:05+00:00" } ];
var arrayOne = [{ "Name": "Andy"}, { "Name": "Bayer"}, { "Name": "James"}, { "Name": "Doda"}];
var result = arrayOne.map(val=>arrayTwo.find(p=>p.Name==val.Name) || val);
console.log(result);
Suppose first array name is First
First : any [] = [{"Name": "Andy"},{"Name": "Bayer"},{ "Name": "James"},{"Name": "Doda"}]
And Second array name is Second
Second : any[] = [{"Name": "Andy","Id": "1","Time": "2020-06-19T11:02+00:00"},{"Name": "Bayer"},{"Name": "James"},{"Name": "Doda","Id": "4","Time": "2020-06-19T11:05+00:00"}]
Now do looping and check each name of first if its exists in second copy from second and push in result array
result : any[] =[];
this.First.forEach((element) => {
let index = this.Second.findIndex((x) => element.Name== x.Name);
if (index > -1) {
let data = {
this.Second[index].Name,
this.Second[index].Id,
this.Second[index].time,
};
this.result.push(data);
}
}

Find nested object based on a parameter in Javascript

I have an array which looks like this:
[
{
"boxes": [
{
"id": 2,
"content": {
"name": "ABC",
"details": "some details for abc"
}
}
]
},
{
"boxes": [
{
"id": 3,
"content": {
"name": "XYZ",
"details": "some details for xyz"
}
},
{
"id": 4,
"content": {
"name": "UVW",
"details": "some details for uvw"
}
}
]
},
{}
]
And I have a variable: let id = 3
I want to be able to search through the nested array "boxes" to find the content property of the object that have the given id. Such that the result is:
{
"name": "XYZ",
"details": "some details for xyz"
}
Till now I have gathered that I can use a combination forEach and .filter do find this. But I'm not sure how. Also, I have control over the original data. So, if there's a better way to store the original data, I will be glad to have suggestions.
Actually I did attempt but got stuck:
Let's say the original array is called house.
let matches = []
let id = 3
house.forEach(function(e) {
matches = matches.concat(e.boxes.filter(function(b) {
return (b.id === id);
}));})
console.log(matches[0].content)
You could use map method by passing a callback function as argument. The scope of map method is to get all items from boxes array.
Also, I'm using filter(Boolean) statement in order to remove undefined value for those items which doesn't have boxes as property.
At least, use find method in order to get the desired output result.
let arr = [ { "boxes": [ { "id": 2, "content": { "name": "ABC", "details": "some details for abc" } } ] }, { "boxes": [ { "id": 3, "content": { "name": "XYZ", "details": "some details for xyz" } }, { "id": 4, "content": { "name": "UVW", "details": "some details for uvw" } } ] }, {} ]
let id = 1;
let result = [].concat(...arr.map(item => item.boxes))
.filter(Boolean)
.find(({id}) => id == id).content;
console.log(result);
First you need to put boxes object into one array, it seems it just has couple extra levels you don't need; You can create a helper function for that.
make a loop with if statement if there is what you are looking for or not
Here I use forEach() to loop through the data and get the boxes.
After that, I use filter() to get the object with the desired id. That's all.
const data = [
{
"boxes": [
{
"id": 2,
"content": {
"name": "ABC",
"details": "some details for abc"
}
}
]
},
{
"boxes": [
{
"id": 3,
"content": {
"name": "XYZ",
"details": "some details for xyz"
}
},
{
"id": 4,
"content": {
"name": "UVW",
"details": "some details for uvw"
}
}
]
},
{}
]
data.forEach(data => {
if (data.boxes){
let searchedData = data.boxes.filter(box => box.id == 3);
if (searchedData.length > 0){
console.log(searchedData[0].content);
}
}
});

Most performant way to sort a deeply nested array of objects by another deeply nested array of objects

As an example - I've included a one element array that contains an object that has a Children key, which is an array of objects and each object also has its' own Children key that contains another array.
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
{
"Id": "100",
"DisplayName": "1-2",
},
]
}
]
}
]
There is a second array of objects that I would like to compare the first array of objects to, with the intention of making sure that the first array is in the same order as the second array of objects, and if it is not - then sort until it is.
Here is the second array:
[
{
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "100",
"DisplayName": "1-2",
},
{
"Id": "10",
"DisplayName": "3-4",
},
{
"Id": "1000",
"DisplayName": "5-6",
},
]
}
]
}
]
The data that this will run on can be up in the tens of thousands - so performance is paramount.
What I'm currently attempting is using a utility method to convert each element of the second array into a keyed object of objects e.g.
{
1: {
"Id": "1",
"Children": [
{
"Id": "2",
"Children": [
{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}
]
}
}
This allows fast look up from the top level. I'm wondering if I should continue doing this all the way down or if there is an idiomatic way to accomplish this. I considered recursion as well.
The order of the already sorted array is not based on Id - it is arbitrary. So the order needs to be preserved regardless.
Assuming same depth and all Id's exist in each level of each object use a recursive function that matches using Array#findIndex() in sort callback
function sortChildren(main, other) {
other.forEach((o, i) => {
if (o.children) {
const mChilds = main[i].children, oChilds = o.children;
oChilds.sort((a, b) => {
return mChilds.findIndex(main => main.Id === a.Id) - mChilds.findIndex(main => main.Id === b.Id)
});
// call function again on this level passing appropriate children arrays in
sortChildren(mChilds, oChilds)
}
})
}
sortChildren(data, newData);
console.log(JSON.stringify(newData, null, ' '))
<script>
var data = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "3",
"DisplayName": "1-2",
},
{
"Id": "4",
"DisplayName": "3-4",
},
]
}]
}]
var newData = [{
"Id": "1",
"Children": [{
"Id": "2",
"Children": [{
"Id": "4",
"DisplayName": "3-4",
},
{
"Id": "3",
"DisplayName": "1-2",
},
]
}]
}]
</script>

Working With Dynamic Multidimensional key-value pairs in JSON

Having a thorny problem and only see similar but also simpler solutions on SO.
Is is possible to generate a dynamic key AND dynamic values using JS/JSON?
For instance, let's say I have JSON like this:
{
"email": "user#someco.com",
"firstname": "Bob",
"lastname": "Smith",
"company": "ACME",
"custom": {
"services": [
{
"name": "svc1",
"desc": "abcdefg",
"selected": "true",
"status": "None"
},
{
"name": "svc2",
"desc": "abcdefg",
"selected": "true",
"status": "None"
},
{
"name": "svc3",
"desc": "abcdefg",
"selected": "false",
"status": "None"
},
{
"name": "svc4",
"desc": "abcdefg",
"selected": "false",
"status": "None"
}
],
"fields": [
{
"name": "Products",
"desc": "abcdef",
"type": "multi",
"values": [
{
"name": "Product1",
"desc": "abcdef"
},
{
"name": "Product2",
"desc": "abcdef"
}
],
"services": [
"svc1",
"svc2",
"svc3"
]
},
{
"name": "Wines",
"desc": "abcdef",
"type": "multi",
"values": [
{
"name": "Wine 1",
"desc": "abcdef"
}
],
"services": [
"svc4"
]
},
{
"name": "Fruits",
"desc": "abcdef",
"type": "multi",
"values": [
{
"name": "Fruit 1",
"desc": "abcdef"
},
{
"name": "Fruit 2",
"desc": "abcdef"
}
],
"services": [
"svc4"
]
}
]
}
};
I need to go into the fields and for each field (products, wines, fruits) see if a given service is contained within so that I can go back and generate a product or wine or fruit for each service that requires it. But I don't want to repeat the services names more than once. The resulting JSON should look something like this:
{"svc1":["Products"], "svc2":["Products"], "svc3":["Products"], "svc4":["Fruits", "Wines"]}
The hope would be that to generate a dynamic list in Angular I can just turn and loop back through this JSON, pulling out the values for each product, fruit, wine, whatever.
I've been trying a lot of nested for loops and the like but whenever I get more than one layer down the dynamism seems to stop. I'm guessing that for this to work I need to move between JS Objects and JSON?
Right now I'm trying something like this, which isn't quite working, stringify or no. And maybe I'm flip-flopping too much between JSON and JS Objects:
var outObj = [];
var fieldItems;
$.each(jsonObj.custom.fields, function(key, item) {
fieldItems = item;
fieldItems.name = item.name;
$.each(fieldItems.services, function(key, item) {
var serviceName = item;
//check to see if the serviceName already exists
if (outObj.indexOf(serviceName) > -1) {
outObj.serviceName.push(fieldItems.name);
} else {
outObj.push(serviceName);
}
});
});
JSON.stringify(outObj);
console.log("outObj " + outObj);
I get "can't read property 'push' of undefined" errors and the like. Seems this should be possible from a single nested loop, but maybe I need to just do two passes? Any other suggestions?
To me it sounds like overcomplicated solution. You can use basic array methods of javascript to filter out required structure. I am not sure what profiling_value in the presented snippet, so I started from the object structure in OP
var desiredResult = jsonObj.custom.services.reduce(function(result, service){
result[service.name] = jsonObj.custom.fields.filter(function(field){
return field.services.indexOf(service.name) >= 0;
}).map(function(field){ return field.name; });
return result;
}, {});
This gives the expected result for mentioned object.
reduce is required to iterate over all services and accumulate result in one object. Then for each service fields are iterated to filter out only those that contain link to this service. And finally list of filtered fields is transformed (map) into list of strings - their names - and inserted into accumulator

Categories

Resources