I have a JSON that has three levels and it's stored as Array I need to get 2nd level(count started from 0) and below in each level has 10+ elements. How it can be implemented on JavaScript.Need your help. All response will be helpful.
ps. some 1st level and 2nd level elements can be empty
[
{
"name": "0th level first", //0th level
"options": [
{
"name": "1st level Cafe", // 1st level
"options": [
{
"name": "2nd level Staff", //2nd level
"options": [
{
"name": "Gary", //3rd level
"age": 18
},
{
"name": "James", //3rd level
"age": 25
}
]
}
]
}
]
}
]
Probably this one? :)
data.forEach((value, index) => {
for(stage0 in value){
if(typeof value[stage0] === 'object'){
value[stage0].forEach((val, index) => {
for(stage1 in val){
if(typeof val[stage1] === 'object'){
val[stage1].forEach((val2, index) => {
for(stage2 in val2){
console.log(val2[stage2]);
}
})
}
}
})
}
}
})
You can use array.forEach which will be only iterating through each level.
on first loop there is a property call options which is an array, you need to loop through the options array in 0 level, on the second loop again one more options array comes you need to again loop through the options array which is 1 level.
then you reach the thrid level which is your output.
Third level means starting from zero its second level.
I hope this will solve the issue.
var data = [
{
"name": "0th level first", //0th level
"options": [
{
"name": "1st level Cafe", // 1st level
"options": [
{
"name": "2nd level Staff", //2nd level
"options": [
{
"name": "Gary", //3rd level
"age": 18
},
{
"name": "James", //3rd level
"age": 25
}
]
}
]
}
]
}
]
data.forEach(fl => {
fl.options.forEach(sl => {
sl.options.forEach(tl => {
console.log("second level starting from 0",tl)
})
})
})
Assume data contains your json, we have 2 onliners:
let out = data[0].options[0].options[0];
let outArr = data.flatMap(x=>x.options.flatMap(y=>y.options));
results
// out = {"name":"2nd level Staff","options":[{"name":"Gary","age":18},{"name":"James","age":25}]}
// outArr = [{"name":"2nd level Staff","options":[{"name":"Gary","age":18},{"name":"James","age":25}]}]
First one (out) contains one 2nd element. We use here tree indexes here (three zeros) first data[0] return first element in array data, then options[0] return first element in options array.
The second solution (outArr) contains array of all 2nd elements. We use here JS flatMap
To write second solution inspired me abdullahkady comment below question.
Related
How can I iteratively render nested components in react?
(as I understood, recursion is more expensive in terms of performance?)
P.S. nesting levels can be unlimited
Example:
"comments": [
{
"name": "1comment",
"body": "1comment",
"date": "2019-05-15T15:56:15.694116Z",
"id": "0179ef41-fdb6-4700-a4dc-6d7bbc54385a",
"parent": null,
"reply": []
},
{
"name": "2comment",
"body": "2comment",
"date": "2019-05-17T13:59:51.167188Z",
"id": "1ef06878-58b5-48b0-9349-73986ab66bb4",
"parent": null,
"reply": [
{
"name": "2-1-comment",
"body": "2-1-comment",
"date": "2019-05-21T22:32:44.998207Z",
"id": "514aa634-08bd-4ca3-8a1a-eb10846808ed",
"parent": "5a01211d-3ee9-4bf6-9a50-462a8277898a",
"reply": [
{
"name": "2-1-1-comment",
"body": "2-1-1-comment",
"date": "2019-05-21T22:32:44.998207Z",
"id": "514aa634-08bd-4ca3-8a1a-eb10846808ed",
"parent": "5a01231d-3119-4bf6-9a50-462a8277898a",
"reply": []
}
]
}
]
},
{
"name": "3comment",
"body": "3comment",
"date": "2019-05-19T12:07:15.613266Z",
"id": "5a01231d-3ee9-4bf6-9a50-462a8277898a",
"parent": null,
"reply": [
{
"name": "3-1-comment",
"body": "3-1-comment",
"date": "2019-05-21T22:32:44.998207Z",
"id": "514aa634-08bd-4ca3-8a1a-eb10846808ed",
"parent": "5a01231d-3ee9-4bf6-9a50-462a8277198a",
"reply": []
}
]
}
]
The basic principle would be to store the "path" to the current element in some way, then
push if there is a child,
iterate over current list,
pop if the current list is done.
E.g. stack = [ 2, 1, 3 ] in your case would represent element.reply[ 2 ].reply[ 1 ].reply[ 3 ].
I believe you have to store as well the lengths of all the lists of each path (to be able to iterate every level), which could be stored directly in the stack, but that in turn makes it more confusing to get back the index when you need it.
E.g.:
const stack = [ comments.length ];
while( stack.length > 0 ){
// -- get current item by the "path" (i.e. by the stack)
const currentItem = stack.reduce( (acc, value, index ) => {
const list = index === 0 ? acc : acc.reply; // root or not root
return list[ list.length - value ];
}, comments);
// --
if( currentItem ){
console.log( stack, currentItem && currentItem.name );
if ( currentItem.reply && currentItem.reply.length > 0 ) {
stack.push(currentItem.reply.length); // level deeper
} else {
stack[stack.length - 1]--; // next sibling
}
} else {
stack.pop(); // level up
stack[stack.length - 1]--; // next sibling
}
}
About the performance:
I guess the iterative approach becomes necessary only for really huge amounts of data. For 'normal' data the recursion should be able to keep a structure of 5 or even 50 levels deep in memory. E.g. I used the iterative approach once for millions of XML lines (not limited, so theoretically infinite), deeply nested. I guess for thousands I would still have used recursion.
Tip: Do you use a pattern like acc = [ ...acc, item ] (whick is common in React) ? That becomes slow at a scale of hundreds or thousands. Then you might want to have a look at Is mutating accumulator in reduce function considered bad practice
I am trying in Javascript, using PUG template (if possible), to compare two arrays and when I find a correspondance in IDs, display some particular elements.
// First Array : I iterate over "hearts" object
// Called in PUG : - const user
[
{
"hearts": [
"5e70c63a94b27b164c9b897f",
"5e723c75e4bfdf4f58c55e32"
],
"_id": "5e6bb1189978fd5afc98c57a",
"email": "catherine#catherine.com",
"name": "Catherine",
"photo": "0121b7fe-b2ae-4e75-979d-7dea1a432855.jpeg",
"__v": 0
},
{
"hearts": [
"5e723c75e4bfdf4f58c55e32"
],
"_id": "5e6bc41f5915e3d2980a5174",
"email": "marc#marc.com",
"name": "Marc",
"photo": "4caa7bfb-6408-4893-a78b-fa6e8e5b03e7.png",
"__v": 0
}
]
// Second array : I iterate over "author.hearts" object
// Called in PUG : - const store
[{
"product": {
"categories": [
1,
2
]
},
"_id": "5e6bcc76c4022eae00e22af6",
"date": "2222-02-20T21:22:00.000Z",
"author": {
"hearts": [
"5e723c75e4bfdf4f58c55e32",
"5e70c63a94b27b164c9b897f"
],
"_id": "5e6bb1189978fd5afc98c57a",
"__v": 0
},
"created": "2020-03-13T18:09:58.086Z",
"id": "5e6bcc76c4022eae00e22af6"
}]
I want to loop over the first array, find the first ID (here 5e70c63a94b27b164c9b897f), loop over the second array and see if this ID is present within the "author.hearts" object. If it is not, carry on with the second ID and if it is present, display all the keys (tags, photos, _id, date...) from the object where the ID was found.
In my example, I have just one object in my array, but I'll be having much more later on.
Many thanks for your help
If I'm understanding correctly you can do something like this. Loop through all your users and when you find their id in author.hearts stop the loop there and return the object the user's _id was found in.
var resultFound = undefined;
try {
user.forEach((el) => {
const id = el._id;
const result = store.find(el => el.author.hearts.includes(id));
if (result) {
resultFound = result;
throw resultFound;
}
});
} catch (e) {
if (e !== resultFound) {
throw e;
}
}
I want to fetch all the names and label from JSON without loop. Is there a way to fetch with any filter method?
"sections": [
{
"id": "62ee1779",
"name": "Drinks",
"items": [
{
"id": "1902b625",
"name": "Cold Brew",
"optionSets": [
{
"id": "45f2a845-c83b-49c2-90ae-a227dfb7c513",
"label": "Choose a size",
},
{
"id": "af171c34-4ca8-4374-82bf-a418396e375c",
"label": "Additional Toppings",
},
],
},
]
}
When you say "without loops" I take it as without For Loops. because any kind of traversal of arrays, let alone nested traversal, involve iterating.
You can use the reduce method to have it done for you internally and give you the format you need.
Try this :
const data = {
sections: [
{
id: "62ee1779",
name: "Drinks",
items: [
{
id: "1902b625",
name: "Cold Brew",
optionSets: [
{
id: "45f2a845-c83b-49c2-90ae-a227dfb7c513",
label: "Choose a size"
},
{
id: "af171c34-4ca8-4374-82bf-a418396e375c",
label: "Additional Toppings"
}
]
}
]
}
]
};
x = data.sections.reduce((acc, ele) => {
acc.push(ele.name);
otherName = ele.items.reduce((acc2, elem2) => {
acc2.push(elem2.name);
label = elem2.optionSets.reduce((acc3, elem3) => {
acc3.push(elem3.label);
return acc3;
}, []);
return acc2.concat(label);
}, []);
return acc.concat(otherName);
}, []);
console.log(x);
Go ahead and press run snippet to see if this matches your desired output.
For More on info reduce method
In the context of cJSON
yes, we can fetch the key value for any of the object.
1 - each key value is pointed by one of the objects. will simply fetch that object and from there will get the key value.
In the above case for
pre-requisition: root must contain the json format and root must be the cJSON pointer. if not we can define it and use cJSON_Parse() to parse the json.
1st name object is "sections" will use
cJSON *test = cJSON_GetObjectItem(root, "sections");
char *name1 = cJSON_GetObjectItem(test, "name" )->valuestring;
2nd name key value
cJSON *test2 = cJSON_GetObjectItem(test, "items");
char *name2 = cJSON_GetObjectItem(tes2, "name")->valuestring;
likewise, we can do for others as well to fetch the key value.
This is the sample json:
{
"search": {
"facets": {
"author": [
],
"language": [
{
"value": "nep",
"count": 3
},
{
"value": "urd",
"count": 1
}
],
"source": [
{
"value": "West Bengal State Council of Vocational Education & Training",
"count": 175
}
],
"type": [
{
"value": "text",
"count": 175
}
],
}
}
There are several ways to delete key search.facets.source:
delete search.facets.source
delete jsobObj['search']['facets']['source']
var jsonKey = 'source';
JSON.parse(angular.toJson(jsonObj), function (key, value) {
if (key != jsonKey)
return value;
});
Above 1 & 2 are not dynamic, and 3 is one of the way but not a proper way. Because if source is present in another node then it will not work. Please anybody can tell me how to delete it dynamically in any kind of nested key. Because we can not generate sequence of array dynamically in above 2.
Assuming you're starting from this:
let path = 'search.facets.source';
Then the logic is simple: find the search.facets object, then delete obj['source'] on it.
Step one, divide the path into the initial path and trailing property name:
let keys = path.split('.');
let prop = keys.pop();
Find the facets object in your object:
let parent = keys.reduce((obj, key) => obj[key], jsonObj);
Delete the property:
delete parent[prop];
I have found out another solution, it is very easy.
var jsonKey = 'search.facets.source';
eval('delete jsonObj.' + jsonKey + ';');
I am trying to use underscoreJs to manipulate a JavaScript object and having problems doing so.
Here is my example
var data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
};
var res = _(data).chain().
pluck('parent').
flatten().
findWhere(function(item){
item === "user_get"
}).
value();
console.log(res);
Using an element which is a part of data.parent.calls[] (example : "user_get") I would like to extract its parent object, i.e. data.parent[0].
I tried above but always get undefined. I appreciate any help on this.
One of the problems you're having is your use of _.pluck. If you execute _.pluck over an object, it'll go over the keys of the object trying to retrieve the property you specified as the second argument (in this case, 'parent'). 'label' is a string and 'parent' is an array so thus the array that you get as a result is [undefined, undefined]. The rest will then go wrong.
One solution could be as follows:
function findCallIndexInParent(call, parent) {
return _.chain(parent)
.pluck('resources')
.flatten()
.findIndex(function (obj) {
return _.contains(obj.calls, call);
})
.value();
}
function findCall(call, data) {
var parent = data.parent;
return parent[findCallIndexInParent(call, parent)];
}
console.log(findCall('user_get', data));
findCall is just a convenient method that will pass the parent property of data to findCallIndexInParent (that will retrieve the index where call is) and return the desired object with the parent array.
Lodash (a fork of underscore) provides a method to get the property of an object that would have come really handy in here (sadly, underscore doesn't have it).
The explanation of findCallIndexInParent is as follows:
Chain the parent list
pluck the resources array
As pluck maps, it returns a list of lists so a flatten is needed.
Find the index of the element which calls contains call
Return the value (the index) of the object that contains call within parent.
Here's the fiddle. Hope it helps.
This would seem to do the trick.
function findByCall(data, call) {
return _.find(data.parent, function(parent) { //From data.parent list, find an item that
return _.some(parent.resources, function(resource) {//has such parent.resource that it
return _.includes(resource.calls, call); //includes the searched resource.calls item
});
});
}
//Test
var data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
};
console.log(findByCall(data, 'user_get'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
If I understand correctly, you want to get the index of the element in the parent array which has any resource with the specified call.
data = {
"label": "SomeName",
"parent": [{
"id": "parentId",
"resources": [{
"name": "ID1NAME",
"calls": [
"user_get", "user2_post", "user3_delete"
]
}, {
"name": "ID2",
"calls": [
"employee1_get", "employee2_delete", "employee3_update"
]
}]
}]
}
// find the index of a parent
const index = _.findIndex(data.parent, parent =>
// that has any (some) resources
_.some(parent.resources, resource =>
// that contains 'user_get' call in its calls list
_.contains(resource.calls, 'user_get')
)
)
console.log(index) // 0
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
If you want to find the actual parent object, use find instead of findIndex
If you want to find all parent objects matching this call, use filter instead of findIndex