I am trying to remove the first object from a nested array but somehow I am not able to delete the first object here is my code can you please help?
var arr = [
{
demo: [
{
label: "NOT - Notification",
id: "NOT",
subTree: null,
},
{
label: "LIM - Limitation",
id: "LIM",
subTree: null
},
],
},
];
var ind = arr.findIndex(function (element) {
return element.demo?.id === "NOT";
});
if (ind !== -1) {
arr.splice(ind, 1);
}
console.log('this is new', arr);
If you have any better solution then feel free to drop will appreciate your help.
You are accessing the wrong array dimension. Check each subarray in the array:
var arr = [
{
demo: [
{
label: "NOT - Notification",
id: "NOT",
subTree: null,
},
{
label: "LIM - Limitation",
id: "LIM",
subTree: null
},
],
},
];
for (const item of arr) {
const index = item.demo.findIndex(subitem => subitem.id === "NOT");
if (index >= 0) item.demo.splice(index, 1)
}
console.log('this is new', arr);
just add the below snippet:
const newArr = arr.map(data => {
return { demo : data?.demo.filter(d => d.id != "NOT") }
})
console.log(newArr)
Explanation :
Here, I'm looping through each main array, entering into objects, and then filtering only those with id other than "NOT".
Comment if you stuck with anything in the above code.
As per my understanding after looking in your code, You want to filter out the object which contains id as NOT. If Yes, You can simply achieve that by using Array.filter() method.
Live Demo :
var arr = [
{
demo: [
{
label: "NOT - Notification",
id: "NOT",
subTree: null,
},
{
label: "LIM - Limitation",
id: "LIM",
subTree: null
},
],
}];
const res = arr.map(({ demo }) => demo.filter(({ id }) => id !== 'NOT'));
console.log(res);
Related
The data
const data = [
{
id: 1,
title: "Product Red",
inventoryItem: {
inventoryLevels: {
edges: [{ node: { location: { name: "Warehouse Red" } } }],
},
},
},
{
id: 2,
title: "Product Blue",
inventoryItem: {
inventoryLevels: {
edges: [{ node: { location: { name: "Warehouse Blue" } } }],
},
},
},
];
let result = data.filter((product) => {
return product.inventoryItem.inventoryLevels.edges.forEach((inventoryLevel) => {
return inventoryLevel.node.location.name !== "Warehouse Blue";
});
});
console.log(result);
What I want to do is filter by location name. I am not sure how to construct filtering based on nested arrays.
So the result I want is if the location isn't Warehouse Blue. data should just have the object where location name is warehouse red.
You should get your work done using findIndex() instead of your forEach.
This method would search and return the index of your condition, if is not found it will return -1
let result = data.filter(product => product.inventoryItem.inventoryLevels.edges.findIndex(item => item.node.location.name !== "Warehouse Blue") !== -1 )
let result = data.filter((product) => {
return product?.inventoryItem?.inventoryLevels?.edges
.some(edge => edge?.node?.location?.name !== ('Warehouse Blue'))
});
Can use lodash too Lodash: how do I use filter when I have nested Object?
i have an array A
const arrayA = [
{
id:a,
check:false
},
{
id:b,
check:false
},
{
id:c,
check:false
}
and an array B
const arrayB = [
{
id:a,
},
{
id:b,
}
]
and i want to check if arrayB is exist arrayA by id, then change check to true. Using lodash or js array methods
Hopefully I understood your question correctly but this is the solution I came up with.
arrayA.map((item) => ({ ...item, check: arrayB.some(({ id: idB }) => item.id === idB ) }))
You can use nested forEach loops, and check, if id matches then set check to true.
const arrayA = [{
id: "a",
check: false
},
{
id: "b",
check: false
},
{
id: "c",
check: false
}
]
const arrayB = [{
id: "a",
},
{
id: "b",
}
]
arrayB.forEach((b)=>{
arrayA.forEach((a)=>{
if(b.id == a.id){
a.check = true;
}
})
})
console.log(arrayA);
You could create an array containing the ids of arrayB and then check the objects in arrayA like
const arrayA = [
{
id: 'a',
check:false
},
{
id:'b',
check:false
},
{
id:'c',
check:false
} ];
const arrayB = [
{
id:'a',
},
{
id:'b',
}
];
const idsB = arrayB.map( obj => obj.id);
arrayA.forEach(obj => { if(idsB.indexOf(obj.id) > -1) obj.checked = true; } );
arrayA.forEach(obj => {console.log(JSON.stringify(obj))});
I could come up with this, which is not different than double loop, but may read easier.
arrayA.map((a) => {
a.check = arrayB.findIndex((b) => b.id === a.id) != -1;
return a;
});
Try this code it may help you
const arrayA = [
{id:'a',check:false},
{id:'b',check:false},
{id:'c',check:false}
]
const arrayB = [
{id:'a',},
{id:'b',}
]
arrayB.map(i => {
return i.check = arrayA.find(item => i.id == item.id)?.check;
});
console.log(arrayB)
I want to find duplicated objects and add hasDuplicate: true property, but not the first one. Methods should be run after one element.
The example array
items: [
{
checked: false,
desc: "",
id: "396",
value: "Lorem",
},
{
checked: false,
desc: "",
id: "230",
value: "Lorem"
},
{
checked: false,
desc: "",
id: "396",
value: "Lorem",
hasDuplicate: true
},
{
checked: false,
desc: "",
id: "396",
value: "Lorem",
hasDuplicate: true
},
{
checked: false,
desc: "",
id: "230",
value: "Lorem",
hasDuplicate: true
},
]
What is an efficient way to detect duplicate items in an array with ES6?
Use Array.prototype.map() to traverse your array and check whether the hash (Object.entries() concatenated) is already seen:
const src = [{checked:false,desc:"",id:"396",value:"Lorem",},{checked:false,desc:"",id:"230",value:"Lorem"},{checked:false,desc:"",id:"396",value:"Lorem"},{checked:false,desc:"",id:"396",value:"Lorem"},{desc:"",id:"230",checked:false,value:"Lorem"}],
dedupe = (a, hashMap=[]) => a.map(o => {
const hash = Object
.entries(o)
.sort(([a],[b]) =>
a.localeCompare(b))
.flat()
.join('\ud8ff')
return !hashMap.includes(hash) ?
(hashMap.push(hash), o) :
{...o, hasDuplicate: true}
})
console.log(dedupe(src))
.as-console-wrapper{min-height:100%;}
isIdentical() method will compare two objects and returns true if they are identical.
const isIdentical = (obj1, obj2) => {
let flag = true;
Object.keys(obj1).forEach(key => {
if(obj1[key] !== obj2[key]){
flag = false;
return false;
}
});
return flag;
};
Now reduce method will help us loop through the array.
items.reduce((unique, item) => {
const index = unique.findIndex( u => isIdentical(u, item));
if(index < 0){
return [...unique, item];
} else {
item.hasDuplicate = true;
return unique;
}
}, []);
console.log(items);
This sets the hasDuplicate property of every duplicate item except the first one based on their id
items.forEach((d, i) => {
if(i == items.map(d => d.id).indexOf(d.id)) {
d.hasDuplicate = true;
}
})
I have this JSON tree view that represents a menu :
var menus = [
{
label : "1",
items: [
{
label : "1.1"
},
{
label : "1.2",
items : [
{
label : "1.2.1"
},
{
label : "1.2.2"
}
]
},
{
label : "1.3"
},
]
},
{
label : "2"
}
]
I want to mutate this JSON by adding for each item a selected property. This property, a boolean, will be set to true if the label is the right one or this is the tricky part if the descendant is the right one.
For instance, if I'm looking for label 1.2, all labels 1 and 1.2 will be selected. So I will get this JSON :
var menus = [
{
label : "1",
selected : true,
items: [
{
label : "1.1"
selected : false
},
{
label : "1.2",
selected : true,
items : [
{
label : "1.2.1"
selected : false
},
{
label : "1.2.2",
selected : false
}
]
},
{
label : "1.3",
selected : false
},
]
},
{
label : "2",
selected : false
}
]
the selected : false is not necessary.
Lodash is OK for me;)!
Any suggestions?
edit : where I am ! --> https://codepen.io/anon/pen/XGoXjM?editors=0010
edit 2 : finding elements must not be based on the way I wrote the labels. The labels can be any string... Sorry...
Thanks
This solution uses a for loop to iterate recursively the menu items and their children. If an item is selected, it adds selected: true to the item and it's parents:
const selectMenuItems = menus => selectedLabel => {
const internal = arr => {
let selected = false
for(let i = 0; i < arr.length; i++) {
const item = arr[i]
const childrenSelected = !!item.items && internal(item.items)
item.selected = childrenSelected || item.label === selectedLabel
selected = selected || item.selected
}
return selected
}
internal(menus)
return menus
}
const menus = [{"label":"1","items":[{"label":"1.1"},{"label":"1.2","items":[{"label":"1.2.1"},{"label":"1.2.2"}]},{"label":"1.3"}]},{"label":"2"}]
const menuSelector = selectMenuItems(menus)
const result = menuSelector('1.2')
console.log(result)
.as-console-wrapper { top: 0; max-height: 100% !important; }
I would simply check labels this way:
var menus = [{
label: "1",
items: [{
label: "1.1"
},
{
label: "1.2",
items: [{
label: "1.2.1"
},
{
label: "1.2.2"
}
]
},
{
label: "1.3"
},
]
},
{
label: "2"
}
];
var checkSelected = function(items, search) {
for (var key in items) {
items[key].selected = search.startsWith(items[key].label) && items[key].label.length<=search.length;
if (items[key].items) {
checkSelected(items[key].items, search);
};
};
};
var test = "1.2";
checkSelected(menus, test);
console.log(menus);
Also on JSFiddle.
The startsWith() method determines whether a string begins with the
characters of a specified string, returning true or false as
appropriate.
quoted from here
You can use some recursive approach to implement this.
let str = '1.2.1';
function checkItem(arr, strArr) {
// iterate over the array
arr.forEach((obj) => {
// set selected property based on matching every digit in label in same order
// if digits would be single then you can use startsWith and no need to split string
obj.selected = obj.label.split('.').every((it, i) => it === strArr[i]);
// if nested item is there then call recursively
obj.items && checkItem(obj.items, strArr);
});
return arr;
}
checkItem(menus, str.split('.'));
var menus = [{
label: "1",
items: [{
label: "1.1"
},
{
label: "1.2",
items: [{
label: "1.2.1"
},
{
label: "1.2.2"
}
]
},
{
label: "1.3"
},
]
},
{
label: "2"
}
];
let str = '1.2.1';
function checkItem(arr, strArr) {
arr.forEach((obj) => {
obj.selected = obj.label.split('.').every((it, i) => it === strArr[i]);
obj.items && checkItem(obj.items, strArr);
});
return arr;
}
checkItem(menus, str.split('.'));
console.log(menus);
UPADATE : Since you want to update selected property completely independent of label you can do something like follows. I assume you want to
update based on position in the array.
let str = '1.2.1';
function checkItem(arr, prefixArray, strArr) {
// iterate over the array
arr.forEach((obj, i) => {
// generate new prefix array for checking
let pa = [...prefixArray, i + 1];
// compare prefix array with the string array to check matches
obj.selected = pa.every((it, i) => it == strArr[i]);
// if items defined do it recursively
obj.items && checkItem(obj.items, pa, strArr);
});
return arr;
}
checkItem(menus,[], str.split('.'));
var menus = [{
label: "1",
items: [{
label: "1.1"
},
{
label: "1.2",
items: [{
label: "1.2.1"
},
{
label: "1.2.2"
}
]
},
{
label: "1.3"
},
]
},
{
label: "2"
}
];
let str = '1.2.1';
function checkItem(arr, prefixArray, strArr) {
arr.forEach((obj, i) => {
let pa = [...prefixArray, i + 1];
obj.selected = pa.every((it, i) => it == strArr[i]);
obj.items && checkItem(obj.items, pa, strArr);
});
return arr;
}
checkItem(menus,[], str.split('.'));
console.log(menus);
This is my array:
[{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
}
}]
I need to find the last element (his index) who has the skill good set to true. The only way I know to fix this is to use a for/if combination. Is there any other faster/optimal way to do it ?
Use filter:
const goodSkills = myArray.filter(x => x.skills.good)
Then get the last item:
goodSkills[goodSkills.length - 1]
Or if you only need the index, and we treat name as a unique key:
const lastGoodIndex = myArray.findIndex(x => x.name === goodSkills[goodSkills.length - 1].name)
You can then use lastGoodIndex for whatever nefarious purpose you have in mind.
Alternatively if name is not a unique key, I suggest just using forEach:
let lastGoodIndex;
myArray.forEach((x, i) => lastGoodIndex = x.skills.good ? i : lastGoodIndex);
console.log(lastGoodIndex);
The fastest way is to us a for loop:
var arr = [{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
},
}]
function findLastGoodIndex(arr) {
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i].skills.good) {
return i;
}
}
}
console.log(findLastGoodIndex(arr));
Or if the list isn't that large you can combine reverse with findIndex:
arr.reverse().findIndex(x => x.skills.good));
You can do it in 3 rows:
var arr = [
{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
},
}
]
var lastIndex;
arr.forEach(function(item, i) {
if(item.skills.good) lastIndex = i;
})
console.log(lastIndex);
If you want the index of last element that has good = true your best option is to use for/if (perhaps going backwards, i.e. i-- if you can guess where can you expect the good item?).
filter will find the item(s) too, and it also has index property, but generally it's slower than for/if.
forEach will also generally be slower than for/if.
edit:styling.
Since you need to search the last value, you should search in descending order. This would prevent any unnecessary iterations.
var data = [{
name: "Test",
skills: {
agile: true,
good: true
}
},
{
name: "Test 2",
skills: {
agile: false,
good: false
}
}
];
var i = data.length;
var result = null;
while (i--) {
if (data[i].skills.good) {
result = i;
break;
}
}
console.log(result);