I need to delete item from an array with nested items. Here's the array,
{
"name": "Main Course",
"items": [
{
"menuname": "Chinese",
"id": "12",
"menu": [
{"name": "Noodles",id="1"},
{"name": "Rice",id="2"},
{"name": "Xinjiang Roast",id="3"}
]
},
{
"menuname": "Indian",
"id": "14",
"menu": [
{"name": "Rice",id="2"},
{"name": "Paratha",id="5"},
{"name": "Dal Fry",id="6"}
]
}
]
}
I need to delete the item say, {"name": "Rice",id="2"}, from the first menu (menuname:Chinese). Please note that the same item appears in another menu (menuname:Indian) too, which I don't want to delete. Looks like the common approach is to find the indexOf the item to delete and splice it. Here's what I tried,
myArray[0].items.forEach(function (val) {
if((val.menuname==="Chinese" && val.id==="12"))
{
val.menu.forEach(function (value) {
if(value.name === "Rice" && value.id==="2"){
myArray.indexOf(value.name);
}
})
}
});
This always returns an index of -1. What am I doing wrong?
Edit:A close vote for unclear question (Seriously?). The code I am using doesn't return the proper index and I asked what is wrong with the code. With all due respect, please spend some time reading the question.
First your JSON string look strange.
Then you can't call myArray[0] because you don't get an array:
var k = {
"name": "Main Course",
"items": [{
"menuname": "Chinese",
"id": "12",
"menu": [{
"name": "Noodles",
"id": "1"
}, {
"name": "Rice",
"id": "2"
}, {
"name": "Xinjiang Roast",
"id": "3"
}]
}, {
"menuname": "Indian",
"id": "14",
"menu": [{
"name": "Rice",
"id": "2"
}, {
"name": "Paratha",
"id": "5"
}, {
"name": "Dal Fry",
"id": "6"
}]
}]
};
k.items.forEach(function (val) {
if (val.menuname === "Chinese" && val.id === "12") {
for (var i = 0; i < val.menu.length; i++) {
if (val.menu[i].name === "Rice" && val.menu[i].id === "2") {
val.menu.splice(i, 1);
}
}
}
});
console.log(k);
http://jsfiddle.net/XmsQR/2/
Related
I've been playing around trying to learn in an API project using Postman and conducting tests using JavaScript. So far, I have succeeded with the help of reading on websites and watching YouTube videos. Of course, previous tests and playing around have been fairly easy but now I came to a stop. I really tried to figure this out for several weeks but I need further guidance, a push in the right direction or direct help.
What I'm trying to do is to filter out some of the response to only view objects that contain specific data.
To do that, I'm using a filter where I want all products containing a specific value inside an array "product_option_values".
My first approach was to see if I could sort products having any values from the first array, and it worked. It filters just fine.
var filterSmall = jsonData.products.filter(fs => fs.associations.product_option_values);
My next approach was to get to my goal of filtering out products according to specific values inside this array. I tried many simple .(dot) combinations and pointing to [index] to access it without any luck. (I must add that I know how to access this from a specific product, but that way doesn't work when filtering).
I've also tried other approaches such as:
var filterSmall = jsonData.products.filter(fs => fs.associations["product_option_values", 0, "name"] === "S");
and other similar combinations.
This is a very shortened sample of the structure of "products" which in its full form consists of 20 products and far more values inside of it:
{
"products": [
{
"id": 16,
"manufacturer_name": "Graphic Corner",
"quantity": "0",
"price": "12.900000",
"indexed": "1",
"name": "Mountain fox notebook",
"associations": {
"categories": [
{
"id": "2"
},
{
"id": "6"
}
],
"product_option_values": [
{
"id": "22"
},
{
"id": "23"
}
]
}
},
{
"id": 17,
"manufacturer_name": "Graphic Corner",
"quantity": "0",
"price": "12.900000",
"indexed": "1",
"name": "Brown bear notebook",
"associations": {
"categories": [
{
"id": "2"
},
{
"id": "6"
}
],
"product_option_values": [
{
"id": "23"
},
{
"id": "24"
}
]
}
}
]
}
and here is a small and expanded sample from product_option_values:
{
"product_option_values": [
{
"id": 1,
"id_attribute_group": "1",
"color": "",
"position": "0",
"name": "S"
},
{
"id": 2,
"id_attribute_group": "1",
"color": "",
"position": "1",
"name": "M"
},
{
"id": 3,
"id_attribute_group": "1",
"color": "",
"position": "2",
"name": "L"
}
]
}
How do I proceed? Did I do anything correct or even close to it?
Perhaps I've been staring at this for too long.
Thanks in advance.
If you want to compare nested attributes you have to transform the objects (e.g. by using a map operation), so that the relevant attributes are easily accessible for a comparison. If you want to filter by product_option_value id, you could do something like this:
const jsonData = {
"products": [
{
"id": 16,
"manufacturer_name": "Graphic Corner",
"quantity": "0",
"price": "12.900000",
"indexed": "1",
"name": "Mountain fox notebook",
"associations": {
"categories": [
{
"id": "2"
},
{
"id": "6"
}
],
"product_option_values": [
{
"id": "22"
},
{
"id": "23"
}
]
}
},
{
"id": 17,
"manufacturer_name": "Graphic Corner",
"quantity": "0",
"price": "12.900000",
"indexed": "1",
"name": "Brown bear notebook",
"associations": {
"categories": [
{
"id": "2"
},
{
"id": "6"
}
],
"product_option_values": [
{
"id": "23"
},
{
"id": "24"
}
]
}
}
]
};
const sample = {
"product_option_values": [
{
"id": 22,
"id_attribute_group": "1",
"color": "",
"position": "0",
"name": "S"
},
{
"id": 2,
"id_attribute_group": "1",
"color": "",
"position": "1",
"name": "M"
},
{
"id": 3,
"id_attribute_group": "1",
"color": "",
"position": "2",
"name": "L"
}
]
};
const ids = sample.product_option_values.map((el) => String(el.id));
console.log(ids);
const filtered = jsonData.products.filter((fs) => fs.associations.product_option_values.map((e) => e.id).some((f) => ids.includes(f)));
console.log(filtered);
I have an object with the structure of:
[
{
"id": "NONSTOPS",
"label": "Nonstop",
"value": "no"
},
{
"id": "REWARDS",
"label": "Rewards",
"value": "no"
},
{
"id": "SEAT",
"label": "Seats",
"value": "no"
},
{
"id": "BAGS",
"label": "Bags",
"value": "no"
}
]
and an array with a structure of
["NONSTOPS", "REWARDS"]
To return
[
{
"id": "NONSTOPS",
"label": "Nonstop",
"value": "yes"
},
{
"id": "REWARDS",
"label": "Rewards",
"value": "yes"
},
{
"id": "SEAT",
"label": "Seats",
"value": "no"
},
{
"id": "BAGS",
"label": "Bags",
"value": "no"
}
]
Based on the array, it would search the object and change the value to "yes" if there exists an ID on the array.
Here's my code so far
for(let i=0; i< obj.length; i++){
for(let j = 0; j<array.length; j++){
if(obj[i].id === array[j]){
obj[i].value ='yes'
}
}
}
Something seems off about my code and I was wondering if there was an easier way to do this, maybe with a mapper of some sorts?
Can probably use a .forEach and an indexOf check on your array (just to be more concise, there's nothing wrong with your approach)
obj.forEach(o => {
if (array.indexOf(o.id) > -1) o.value = "yes";
});
You can use includes
const data = [{
"id": "NONSTOPS",
"label": "Nonstop",
"value": "no"
},
{
"id": "REWARDS",
"label": "Rewards",
"value": "no"
},
{
"id": "SEAT",
"label": "Seats",
"value": "no"
},
{
"id": "BAGS",
"label": "Bags",
"value": "no"
}
]
const lookup = ["NONSTOPS", "REWARDS"];
const reduced = data.map(d => {
return {id: d.id, label: d.label, value: lookup.includes(d.id) ? "YES" : "NO"}
});
console.log(reduced);
You can use a simple for of loop to iterate over the array of objects and then check with the built in includes function if the value exist in the flat array.
const arr = [
{
"id": "NONSTOPS",
"label": "Nonstop",
"value": "no"
},
{
"id": "REWARDS",
"label": "Rewards",
"value": "no"
},
{
"id": "SEAT",
"label": "Seats",
"value": "no"
},
{
"id": "BAGS",
"label": "Bags",
"value": "no"
}
]
const arrToCompare = ["NONSTOPS", "REWARDS"]
for (let obj of arr) {
// you can use includes to find is the value exist in the arrToCompare
if (arrToCompare.includes(obj.id)) {
// if exists then mutate it
obj.value = 'yes';
}
}
console.log(JSON.stringify(arr, null, 4));
In the question you have mentioned that you want to return the valid elements. You can use filter() for the same.
var result = obj.filter(o => (array.indexOf(o.id) > -1) );
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>
This question already has answers here:
Remove duplicates in an object array Javascript
(10 answers)
Closed 5 years ago.
I have an array containing arrays of objects which I need to compare.
I've looked through multiple similar threads, but I couldn't find a proper one that compares multiple arrays of objects (most are comparing two arrays of objects or just comparing the objects within a single array)
This is the data (below is a JSFiddle with code sample)
const data = [
[
{
"id": "65",
"name": "Some object name",
"value": 90
},
{
"id": "89",
"name": "Second Item",
"value": 20
}
],
[
{
"id": "89",
"name": "Second Item",
"value": 20
},
{
"id": "65",
"name": "Some object name",
"value": 90
}
],
[
{
"id": "14",
"name": "Third one",
"value": 10
}
]
]
I want to remove all duplicate arrays of objects, regardless of the length of data (there could be a lot more records).
I managed to get the unique ones extracted into an object:
const unique = data.reduce(function(result, obj) {
return Object.assign(result, obj)
}, [])
That doesn't work for me though, because I need 1 of the duplicated arrays to remain and the returned data to be an array as well, instead of an object. E.g.:
// result I need
[
[
{
"id":"65",
"name":"Some object name",
"value":90
},
{
"id":"89",
"name":"Second Item",
"value":20
}
],
[
{
"id":"14",
"name":"Third one",
"value":10
}
]
]
So how do I compare each array of objects to the others in the parent array and preserve one of each duplicated or unique array of objects?
JSFiddle
you can achieve so by using function.As below. Not sure about best optimum way of doing so.
var testArray = [
[
{
"id": "65",
"name": "Some object name",
"value": 90
},
{
"id": "89",
"name": "Second Item",
"value": 20
}
],
[
{
"id": "89",
"name": "Second Item",
"value": 20
},
{
"id": "65",
"name": "Some object name",
"value": 90
}
],
[
{
"id": "14",
"name": "Third one",
"value": 10
}
]
]
function removeDuplicatesFromArray(arr){
var obj={};
var uniqueArr=[];
for(var i=0;i<arr.length;i++){
if(!obj.hasOwnProperty(arr[i])){
obj[arr[i]] = arr[i];
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
}
var newArr = removeDuplicatesFromArray(testArray);
console.log(newArr);
const data = [
[
{
"id": "65",
"name": "Some object name",
"value": 90
},
{
"id": "89",
"name": "Second Item",
"value": 20
}
],
[
{
"id": "89",
"name": "Second Item",
"value": 20
},
{
"id": "65",
"name": "Some object name",
"value": 90
}
],
[
{
"id": "14",
"name": "Third one",
"value": 10
}
]
];
const temp = {};
const result = [];
data.forEach(itemArr => {
const items = itemArr.filter(item => {
const isUnique = temp[`${item.id}-${item.name}-${item.value}`] === undefined;
temp[`${item.id}-${item.name}-${item.value}`] = true;
return isUnique;
});
if (items.length !== 0)
result.push(items);
});
console.log(result);
I have an object which comprises of a menu.
I want to enter a category ID and get the category name, then move backwards to find it's parents. That's not easy within an object so I'm thinking to catch the parents along the way instead.
The problem I have is how to reset the parents when the end child is not found and there's nowhere else to go.
This is what I'm trying:
var data = [
{
"tree_id": "10",
"name": "babies & children",
"parent": null,
"position": "1"
}, {
"tree_id": "2",
"name": "clothing",
"parent": null,
"position": "1",
"children": [{
"tree_id": "15",
"name": "kids",
"parent": "2",
"position": "3",
"children": [{
"tree_id": "78",
"name": "fourToTen",
"parent": "15",
"position": "3",
"children": [{
"tree_id": "102",
"name": "fourToSix",
"parent": "78",
"position": "3"
}]
}]
}]
}, {
"tree_id": "55",
"name": "toys",
"parent": null,
"position": "1",
"children": [{
"tree_id": "35",
"name": "lego",
"parent": "55",
"position": "3"
}]
}
];
var crumbs = [];
function getParts(data, elem) {
for(var i = 0; i < data.length; i++) {
var obj = data[i];
if(obj.children !== undefined){
/* push parent into crumbs */
crumbs.push(obj.name);
if(obj.children[0].tree_id === elem){
/* if we've found what we're looking, we're done */
crumbs.push(obj.children[0].name);
console.log(crumbs);
} else {
/* reset parents */
crumbs = []; /* <-- this is wrong here */
/* not found, keep recursing */
getParts(obj.children, elem);
}
}
}
}
/* I want this to return
[
"clothing",
"kids",
"fourToTen",
"fourToSix"
]
but it returns
[
"fourToTen",
"fourToSix"
]
*/
getParts(data, '102');
The question is, how can I save the parents array until I'm at the end of the line and the child is not found, and reset it then?
Here's a fiddle if that's your preferred playround
Assuming category id = tree_id and category_name = name
You'll need to treat your data object like a tree, then transverse it and track the parents along the way. If something is found then dump the information you need.
So data is basically an array of objects you will be transversing.
Example:
"use strict";
var data = [
{
"tree_id": "10",
"name": "babies & children",
"parent": null,
"position": "1"
},
{
"tree_id": "2",
"name": "clothing",
"parent": null,
"position": "1",
"children": [{
"tree_id": "15",
"name": "kids",
"parent": "2",
"position": "3",
"children": [{
"tree_id": "78",
"name": "fourToTen",
"parent": "15",
"position": "3",
"children": [{
"tree_id": "102",
"name": "fourToSix",
"parent": "78",
"position": "3"
}]
}]
}]
},
{
"tree_id": "55",
"name": "toys",
"parent": null,
"position": "1",
"children": [{
"tree_id": "35",
"name": "lego",
"parent": "55",
"position": "3"
}]
}
];
// Solution
function transverse(root, tree, targetId) {
tree.push({
catId : root.tree_id,
catName : root.name
});
/* this if() must come first otherwise fails if you want to stop before end */
if (root.tree_id === targetId) {
console.log("Found id:" + targetId+ ", name=" + root.name);
console.log("Dumping parent info => " + JSON.stringify(tree));
return tree;
}
if (root.hasOwnProperty("children") && root.children instanceof Array)
root.children.forEach(child => {
transverse(child, tree, targetId);
});
}
data.forEach(item => {
transverse(item, [], /*Looking for Id=*/"102");
});
console.log("done");
Output:
Found id:102, name=fourToSix
Dumping parent info =>
[
{"catId":"2","catName":"clothing"},
{"catId":"15","catName":"kids"},
{"catId":"78","catName":"fourToTen"},
{"catId":"102","catName":"fourToSix"}]
]
Here's a compact functional way:
data = [{"tree_id":"10","name":"babies & children","parent":null,"position":"1"},{"tree_id":"2","name":"clothing","parent":null,"position":"1","children":[{"tree_id":"15","name":"kids","parent":"2","position":"3","children":[{"tree_id":"78","name":"fourToTen","parent":"15","position":"3","children":[{"tree_id":"102","name":"fourToSix","parent":"78","position":"3"}]}]}]},{"tree_id":"55","name":"toys","parent":null,"position":"1","children":[{"tree_id":"35","name":"lego","parent":"55","position":"3"}]}]
//
first = (ary, fn) => ary.reduce((r, x) => r || fn(x), false);
locate = (data, id) => _locate({children: data}, id, []);
_locate = (node, id, path) => node.tree_id === id ? path
: first(node.children || [], n => _locate(n, id, path.concat(n)));
res = locate(data, '102').map(n => n.name)
console.log(res);