I have an array that has parent-child values. I need to remove the elements from the array if its parent is already there in the array.
Sample data:
const data = [
{
id: "1",
parentIds: []
},
{
id: "2",
parentIds: []
},
{
id: "3",
parentIds: ["1"]
},
{
id: "4",
parentIds: ["1", "3"]
},
{
id: "5",
parentIds: ["6"]
}
]
The expected output is:
[
{
id: "1",
parentIds: []
},
{
id: "2",
parentIds: []
},
{
id: "5",
parentIds: ["6"]
}
]
I tried with the following code:
for (let i = selectedItems.length - 1; i >= 0; i--) {
for (let j = selectedItems.length - 1; j >= 0; j--) {
if (selectedItems[j].parentsIds.includes(selectedItems[i].id)) {
selectedItems.splice(i, 1)
}
}
}
But this fails with Cannot read properties of undefined (reading 'id') after splicing first match.
It there anyway this can be achieved?
First get an array of all id's using map().
Then use filter() to remove those where some() does not includes() in allIds
let data = [{id: "1", parentIds: [] }, {id: "2", parentIds: [] }, {id: "3", parentIds: ["1"] }, {id: "4", parentIds: ["1", "3"] }, {id: "5", parentIds: ["6"] } ];
const allIds = data.map(d => d.id);
data = data.filter((d, x) => !d.parentIds.some(a => allIds.includes(a)));
console.log(data);
You need to .filter your data, to remove all items where .some of the parentIds are found in your data:
const data = [
{ id: "1", parentIds: [] },
{ id: "2", parentIds: [] },
{ id: "3", parentIds: ["1"] },
{ id: "4", parentIds: ["1", "3"] },
{ id: "5", parentIds: ["6"] }
];
const result = data.filter(
row => !row.parentIds.some(
id => data.some(d => d.id === id)
)
);
console.log(result);
First extract an array of ids in the original array:
const ids = data.map(el => el.id)
Then filter the original array intersecting each element's parents array with the extracted ids array:
data.filter(el => el.parentIds.filter(parentId => ids.includes(parentId)).length === 0);
See this question Simplest code for array intersection in javascript for an explanation of how array intersection works.
I have this JSON structure:
const arr = [
{
id: "TaskStatuses",
rows: [
{id: "1", name: "Success"},
{id: "2", name: "Error"},
]
},
{
id: "Objects",
rows: [
{id: "1", name: "Object1"},
{id: "2", name: "Object2"},
]
},
{
id: "Groups",
rows: [
{id: "1", name: "Group1"},
{id: "2", name: "Group2"},
]
},
]
I need to create array with some condition. If my condition correctly I will push elements arr.rows.
Finally i want to get this structure:
[
{
Objects: "Object1",
Groups: "Group1"
},
{
Objects: "Object2",
Groups: "Group2"
}
]
I try to do like this
let sites = []
for (let el in arr) {
if (arr.id == "Objects") {
for (let item of el.rows) {
sites.push({Objects: item.name})
}
}
if (arr.id == "Groups") {
for (let item of el.rows) {
sites.push({Groups: item.name})
}
}
Based on comments and your mentioned expected final object array,
I modified your logic a bit to match the expected output.
My approach uses extra space, but o(nlogn) time (not sure if this is most efficient). Hope this code helps.
let firstArr = [], secondArr = [];
arr.forEach((d, i) => {
if (d.id === "Objects") {
firstArr.push(...d.rows);
}
if (d.id === "Groups") {
secondArr.push(...d.rows);
}
});
//console.log(firstArr, secondArr);
//sorting, if necessary based on id of each arr[i].rows
firstArr.sort(function (a, b) {
return a.id - b.id || a.name.localeCompare(b.name);
});
//sorting, if necessary based on id of each arr[i].rows
secondArr.sort(function (a, b) {
return a.id - b.id || a.name.localeCompare(b.name);
});
let index = 0,
size = Math.min(firstArr.length, secondArr.length);
let finalArr = [];
for (index; index < size; index++) {
if (firstArr[index].id === secondArr[index].id) {
finalArr.push({
Objects: firstArr[index].name,
Groups: secondArr[index].name,
});
}
}
//console.log(finalArr); ////this is your final Array.
Instead of building building an array, building an object would simplify things a lot. You can use the id as key for each object so you can easily find the object you want to attach properties to.
The snippet bellow loops through the different "columns". If the column matches the criteria (being either "Objects" or "Groups"), we'll loop through the rows of the column and attach the properties to the resulting object.
const arr = [{
id: "TaskStatuses",
rows: [
{id: "1", name: "Success"},
{id: "2", name: "Error"},
]
}, {
id: "Objects",
rows: [
{id: "1", name: "Object1"},
{id: "2", name: "Object2"},
]
}, {
id: "Groups",
rows: [
{id: "1", name: "Group1"},
{id: "2", name: "Group2"},
]
}];
const sites = {};
const whitelist = new Set(["Objects", "Groups"]);;
for (const column of arr) {
if (!whitelist.has(column.id)) continue;
for (const row of column.rows) {
sites[row.id] ||= {};
sites[row.id][column.id] = row.name;
}
}
console.log(sites);
I got an array of id's. I also have another array of objects. I would like to remove those objects which match with the array of id's. Below is the pseudo code for the same. Can someone help me with the best approch?
const ids = ['1', '2'];
const objs = [
{
id: "1",
name : "one",
},
{
id: "1",
name : "two"
},
{
id: "3",
name : "three",
},
{
id: "4",
name : "four"
},
];
ids.forEach(id => {
const x = objs.filter(obj => obj.id !== id )
console.log('x ==', x);
});
Use filter and includes method
const ids = ["1", "2"];
const objs = [
{
id: "1",
name: "one",
},
{
id: "1",
name: "two",
},
{
id: "3",
name: "three",
},
{
id: "4",
name: "four",
},
];
const res = objs.filter(({ id }) => !ids.includes(id));
console.log(res);
You can put the ids in a Set and use .filter to iterate over the array of objects and .has to check if the id is in this set:
const ids = ['1', '2'];
const objs = [
{ id: "1", name : "one" },
{ id: "1", name : "two" },
{ id: "3", name : "three" },
{ id: "4", name : "four" },
];
const set = new Set(ids);
const arr = objs.filter(obj => !set.has(obj.id));
console.log(arr);
1st requirement -> you have to check for all elements in id array
way to do that using array's built in method is array.includes() or indexof methods
2nd Requirement -> pick out elements not matching with ur 1st requirement which means filter the array.
Combile two
arr = arr.filter(x => !ids.includes(x.id))
Cool es6 destructung syntax
arr = arr.filter(({id}) => !ids.includes(id))
const ids = ['1', '2'];
const objs = [
{
id: "1",
name : "one",
},
{
id: "1",
name : "two"
},
{
id: "3",
name : "three",
},
{
id: "4",
name : "four"
},
];
let arr = objs.filter(function(i){
return ids.indexOf(i.id) === -1;
});
console.log(arr)
This is my Array of objects. I want to filter the objects by passing query in a function.
const products = [{
name: "A",
color: "Blue",
size: 50
},
{
name: "B",
color: "Blue",
size: 60
},
{
name: "C",
color: "Black",
size: 70
},
{
name: "D",
color: "Green",
size: 50
}
];
My desired output which will filter from the query which I am passing in function which can be anything
{
name: "A",
color: "Blue",
size: 50
}, {
name: "C",
color: "Black",
size: 70
}
This is my query object which I will pass to function
const filter = {
color: ["Blue", "Black"],
size: [70, 50]
};
This is my function which I can assign to other variable and use it for further operations
const filteredData = filterIt(products, filter);
Here is another option:
function filterIt(products, filter) {
return products.filter(p => {
for (const k in filter) {
if (filter[k].indexOf(p[k]) === -1) {
return false;
}
}
return true;
})
}
See a Demo
If you want to make an OR instead of an AND in the logic the previous function modified would be:
function filterIt(products, filter) {
return products.filter(p => {
for (const k in filter) {
// if a value of any property of the product is contained in a filter array, include it
if (filter[k].indexOf(p[k]) !== -1) {
return true;
}
}
return false;
})
}
See a Demo for this one too :)
You could get the entries of the filter object and take key and value for checking with includes.
const
filterBy = filter => o => Object.entries(filter).every(([k, v]) => v.includes(o[k])),
filterIt = (array, filter) => array.filter(filterBy(filter)),
products = [{ name: "A", color: "Blue", size: 50 }, { name: "B", color: "Blue", size: 60 }, { name: "C", color: "Black", size: 70 }, { name: "D", color: "Green", size: 50 }],
filter = { color: ["Blue", "Black"], size: [70, 50] },
filteredData = filterIt(products, filter);
console.log(filteredData);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const products = [{
name: "A",
color: "Blue",
size: 50
},
{
name: "B",
color: "Blue",
size: 60
},
{
name: "C",
color: "Black",
size: 70
},
{
name: "D",
color: "Green",
size: 50
}
];
const filter = {
color: ["Blue", "Black"],
size: [70, 50]
};
let filterIt = (products, filter) => {
return products.filter(p => {
//get all filter keys
let keys = Object.keys(filter);
let validkeys = [];
keys.forEach(k=>{
// check if filter property exist and includes filter value
if(p.hasOwnProperty(k) && filter[k].includes(p[k])){
validkeys.push(true);
}
})
//check all filter matches
return validkeys.length === keys.length;
});
}
const filteredData = filterIt(products, filter);
console.log("...filteredData", filteredData);
Assuming an array of objects as follows:
const listOfTags = [
{id: 1, label: "Hello", color: "red", sorting: 0},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
{id: 5, label: "Hello", color: "red", sorting: 6},
]
A duplicate entry would be if label and color are the same. In this case Objects with id = 1 and id = 5 are duplicates.
How can I filter this array and remove duplicates?
I know solutions where you can filter against one key with something like:
const unique = [... new Set(listOfTags.map(tag => tag.label)]
But what about multiple keys?
As per request in comment, here the desired result:
[
{id: 1, label: "Hello", color: "red", sorting: 0},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
]
Late one, but I don't know why nobody suggests something much simpler:
listOfTags.filter((tag, index, array) => array.findIndex(t => t.color == tag.color && t.label == tag.label) == index);
You could use a Set in a closure for filtering.
const
listOfTags = [{ id: 1, label: "Hello", color: "red", sorting: 0 }, { id: 2, label: "World", color: "green", sorting: 1 }, { id: 3, label: "Hello", color: "blue", sorting: 4 }, { id: 4, label: "Sunshine", color: "yellow", sorting: 5 }, { id: 5, label: "Hello", color: "red", sorting: 6 }],
keys = ['label', 'color'],
filtered = listOfTags.filter(
(s => o =>
(k => !s.has(k) && s.add(k))
(keys.map(k => o[k]).join('|'))
)
(new Set)
);
console.log(filtered);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const listOfTags = [
{id: 1, label: "Hello", color: "red", sorting: 0},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
{id: 5, label: "Hello", color: "red", sorting: 6},
]
const unique = [];
listOfTags.map(x => unique.filter(a => a.label == x.label && a.color == x.color).length > 0 ? null : unique.push(x));
console.log(unique);
One way is create an object (or Map) that uses a combination of the 2 values as keys and current object as value then get the values from that object
const listOfTags = [
{id: 1, label: "Hello", color: "red", sorting: 0},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
{id: 5, label: "Hello", color: "red", sorting: 6},
]
const uniques = Object.values(
listOfTags.reduce((a, c) => {
a[c.label + '|' + c.color] = c;
return a
}, {}))
console.log(uniques)
I would tackle this by putting this into temporary Map with a composite key based on the properties you're interested in. For example:
const foo = new Map();
for(const tag of listOfTags) {
foo.set(tag.id + '-' tag.color, tag);
}
Based on the assumption that values can be converted to strings, you can call
distinct(listOfTags, ["label", "color"])
where distinct is:
/**
* #param {array} arr The array you want to filter for dublicates
* #param {array<string>} indexedKeys The keys that form the compound key
* which is used to filter dublicates
* #param {boolean} isPrioritizeFormer Set this to true, if you want to remove
* dublicates that occur later, false, if you want those to be removed
* that occur later.
*/
const distinct = (arr, indexedKeys, isPrioritizeFormer = true) => {
const lookup = new Map();
const makeIndex = el => indexedKeys.reduce(
(index, key) => `${index};;${el[key]}`, ''
);
arr.forEach(el => {
const index = makeIndex(el);
if (lookup.has(index) && isPrioritizeFormer) {
return;
}
lookup.set(index, el);
});
return Array.from(lookup.values());
};
Sidenote: If you use distinct(listOfTags, ["label", "color"], false), it will return:
[
{id: 1, label: "Hello", color: "red", sorting: 6},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
]
You can use reduce here to get filtered objects.
listOfTags.reduce((newListOfTags, current) => {
if (!newListOfTags.some(x => x.label == current.label && x.color == current.color)) {
newListOfTags.push(current);
}
return newListOfTags;
}, []);
const keys = ['label', 'color'],
const mySet = new Set();
const duplicateSet = new Set();
const result = objList.filter((item) => {
let newItem = keys.map((k) => item[k]).join("-");
mySet.has(newItem) && duplicateSet.add(newItem);
return !mySet.has(newItem) && mySet.add(newItem);
});
console.log(duplicateSet, result);
This can be used to filter duplicate and non duplicate
const listOfTags = [
{id: 1, label: "Hello", color: "red", sorting: 0},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
{id: 5, label: "Hello", color: "red", sorting: 6},
];
let keysList = Object.keys(listOfTags[0]); // Get First index Keys else please add your desired array
let unq_List = [];
keysList.map(keyEle=>{
if(unq_List.length===0){
unq_List = [...unqFun(listOfTags,keyEle)];
}else{
unq_List = [...unqFun(unq_List,keyEle)];
}
});
function unqFun(array,key){
return [...new Map(array.map(o=>[o[key],o])).values()]
}
console.log(unq_List);
We can find the unique value by the below script, we can expand the array using forEach loop and check the value exists on the new array by using some() method and after that create the new array by using push() method.
const arr = [{ id: 1 }, { id: 2 }, { id: 4 }, { id: 1 }, { id: 4 }];
var newArr =[];
arr.forEach((item)=>{
if(newArr.some(el => el.id === item.id)===false){
newArr.push(item);
}
}
);
console.log(newArr);
//[{id: 1}, {id: 2}, {id: 4}];
const listOfTags = [
{id: 1, label: "Hello", color: "red", sorting: 0},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
{id: 5, label: "Hello", color: "red", sorting: 6},
];
const objRes=listOfTags.filter((v,i,s)=>s.findIndex(v2=>['label','color'].every(k=>v2[k]===v[k]))===i);
console.log(objRes);
Maybe helpful. Extract duplicate items from array then delete all duplicates
// Initial database data
[
{ key: "search", en:"Search" },
{ key: "search", en:"" },
{ key: "alert", en:"Alert" },
{ key: "alert", en:"" },
{ key: "alert", en:"" }
]
// Function called
async function removeDuplicateItems() {
try {
// get data from database
const { data } = (await getList());
// array reduce method for obj.key
const reduceMethod = data.reduce((x, y) => {
x[y.key] = ++x[y.key] || 0;
return x;
}, {});
// find duplicate items by key and checked whether "en" attribute also has value
const duplicateItems = data.filter(obj => !obj.en && reduceMethod[obj.key]);
console.log('duplicateItems', duplicateItems);
// remove all dublicate items by id
duplicateItems.forEach(async (obj) => {
const deleteResponse = (await deleteItem(obj.id)).data;
console.log('Deleted item: ', deleteResponse);
});
} catch (error) {
console.log('error', error);
}
}
// Now database data:
[
{ key: "search", en:"Search" },
{ key: "alert", en:"Alert" }
]
One solution is to iterate the array and use a Map of maps to store the value-value pairs that have been encountered so far.
Looking up duplicates this way should be reasonably fast (compared to nested loops or .filter + .find approach).
Also the values could be any primitive type; they are not stringified or concatenated for comparison (which could lead to incorrect comparison).
const listOfTags = [
{id: 1, label: "Hello", color: "red", sorting: 0},
{id: 2, label: "World", color: "green", sorting: 1},
{id: 3, label: "Hello", color: "blue", sorting: 4},
{id: 4, label: "Sunshine", color: "yellow", sorting: 5},
{id: 5, label: "Hello", color: "red", sorting: 6}
];
let map = new Map();
let result = [];
listOfTags.forEach(function(obj) {
if (map.has(obj.label) === false) {
map.set(obj.label, new Map());
}
if (map.get(obj.label).has(obj.color) === false) {
map.get(obj.label).set(obj.color, true);
result.push(obj)
}
});
console.log(result);