Matching array of objects with array of values - javascript

If I have 2 arrays:
arr1: ["1","2"]
arr2: [{id: 1, color: "green"}, {id: 2, color: "yellow"}, {id: 3, color: "red"}]
And I want to obtain:
result: [{id: 1, color: "green"}, {id: 2, color: "yellow"}]
I am trying with
arr2.filter(
e => arr1.indexOf(e.id) !== -1
);
But result is empty.

The problem with your code is that as your inputs have different types(id is a number but arr1 has strings), this arr1.indexOf(e.id) !== -1 will always be false.
Convert one of them to a number like this:
const arr1 = ["1","2"]
const arr2 = [{id: 1, color: "green"}, {id: 2, color: "yellow"}, {id: 3, color: "red"}]
const result = arr2.filter(el => arr1.some(aEl => el.id === +aEl))
console.log(result)
Alternatively, you can convert the ids to strings and use your existing code:
const arr1 = ["1", "2"],
arr2 = [{
id: 1,
color: "green"
}, {
id: 2,
color: "yellow"
}, {
id: 3,
color: "red"
}],
result = arr2.filter(
e => arr1.indexOf(e.id.toString()) !== -1
);
console.log(result)

You could use Array.prototype.filter() with Array.prototype.some() method to get the result.
const arr1 = ['1', '2'];
const arr2 = [
{ id: 1, color: 'green' },
{ id: 2, color: 'yellow' },
{ id: 3, color: 'red' },
];
const ret = arr2.filter((x) => arr1.some((y) => +y === x.id));
console.log(ret);
Another solution using Set Object.
const arr1 = ['1', '2'];
const arr2 = [
{ id: 1, color: 'green' },
{ id: 2, color: 'yellow' },
{ id: 3, color: 'red' },
];
const set = new Set(arr1);
const ret = arr2.filter((x) => set.has(x.id.toString()));
console.log(ret);

Another solution is may be with .includes() method.
const arr1 = ["1","2"]
const arr2 = [{id: 1, color: "green"}, {id: 2, color: "yellow"}, {id: 3, color: "red"}]
const result = arr2.filter(n => arr1.includes(String(n.id)));
console.log(result);

Related

Update array of objects with array of objects

My apologies if this has been addressed before, but I couldn't get it to work with anything I found.
Assume I have 2 arrays - arr1, arr2. I want to update the objects in arr1 if the the property id matches in arr1 and arr2. Objects that exist in arr2 but not in arr1 - meaning the property id does not exist in arr1 - should be pushed to arr1.
Example:
let arr1 = [
{id: 0, name: "John"},
{id: 1, name: "Sara"},
{id: 2, name: "Domnic"},
{id: 3, name: "Bravo"}
]
let arr2 = [
{id: 0, name: "Mark"},
{id: 4, name: "Sara"}
]
# Expected Outcome
let outcome = [
{id: 0, name: "Mark"},
{id: 1, name: "Sara"},
{id: 2, name: "Domnic"},
{id: 3, name: "Bravo"},
{id: 4, name: "Sara"}
]
You can use reduce and find for this:
const arr1 = [
{id: 0, name: "John"},
{id: 1, name: "Sara"},
{id: 2, name: "Domnic"},
{id: 3, name: "Bravo"}
];
const arr2 = [
{id: 0, name: "Mark"},
{id: 4, name: "Sara"}
];
arr2.reduce((res, item) => {
const existingItem = res.find(x => x.id === item.id);
if (existingItem) { existingItem.name = item.name; }
else { res.push(item); }
return res;
}, arr1);
console.log(arr1);
You could do this:
let arr1 = [
{id: 0, name: "John"},
{id: 1, name: "Sara"},
{id: 2, name: "Domnic"},
{id: 3, name: "Bravo"}
]
let arr2 = [
{id: 0, name: "Mark"},
{id: 4, name: "Sara"}
]
var res = arr1.reduce((acc, elem)=>{
var x = arr2.find(i=>i.id === elem.id);
if(x){
acc.push(x)
}else{
acc.push(elem)
}
return acc
}, []);
console.log(res)
Assuming you want to mutate the objects in arr1 rather than creating new ones, one way to do it would be using for...of to iterate the objects in arr2 and then check if there's already an object with the same id in arr1 using Array.prototype.find():
If there is one, you mutate it with Object.assign.
Otherwise, push the new object to arr1:
const arr1 = [
{ id: 0, name: 'John' },
{ id: 1, name: 'Sara' },
{ id: 2, name: 'Domnic' },
{ id: 3, name: 'Bravo' },
];
const arr2 = [
{ id: 0, name: 'Mark', sometingElse: 123 },
{ id: 2, foo: 'bar' },
{ id: 4, name: 'Sara' },
];
for (const currentElement of arr2) {
let previousElement = arr1.find(el => el.id === currentElement.id);
if (previousElement) {
Object.assign(previousElement, currentElement);
} else {
arr1.push(currentElement);
}
}
console.log(arr1);
.as-console-wrapper {
max-height: 100% !important;
}
if you want to try something different you can use foreach and filter to achieve this
let arr1 = [
{id: 0, name: "John"},
{id: 1, name: "Sara"},
{id: 2, name: "Domnic"},
{id: 3, name: "Bravo"}
]
let arr2 = [
{id: 0, name: "Mark"},
{id: 4, name: "Sara"}]
arr1.forEach(x=>{
arr2.forEach(y=>{
if(x.id==y.id){
x.name=y.name
}
})
})
arr2.filter((a)=>{if(!arr1.some(b=>a.id==b.id)) arr1.push(a)})
console.log(arr1)
You should be able to use Array.prototype.find to sort this out!
let arr1 = [
{id: 0, name: "John"},
{id: 1, name: "Sara"},
{id: 2, name: "Domnic"},
{id: 3, name: "Bravo"}
];
let arr2 = [
{id: 0, name: "Mark"},
{id: 4, name: "Sara"}
];
let updateArrayOfObjects = (arr1, arr2) => {
for (let obj of arr2) {
let item = arr1.find(v => v.id === obj.id);
if (item) item.name = obj.name;
else arr1.push({ ...obj });
}
return arr1;
};
console.log(updateArrayOfObjects(arr1, arr2));

Filtering using two array of objects using JavaScript

I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));

How to filter an array of object with an array of numbers

Given an array of objects arr1 how can I filter out to a new array the objects that do not have a property equal to any value in the array of numbers arr2
const arr1 = [
{
key: 1,
name: 'Al'
},
{
key: 2,
name: 'Lo'
},
{
key: 3,
name: 'Ye'
}
];
const arr2 = [2, 3]
// Failed attempt
const newArr = arr1.filter(obj1 => arr2.some(num1 => num1 !== obj1.key))
console.log(newArr)
// Expected: [{ key: 1, name: 'Al' }]
// Received: [
// { key: 1, name: 'Al' },
// { key: 2, name: 'Lo' },
// { key: 3, name: 'Ye' }
// ]
Using your syntax:
You have to match on the somein case it's the same and not different. Then if it matches, do not keep the value.
const arr1 = [
{
key: 1,
name: 'Al',
},
{
key: 2,
name: 'Lo',
},
{
key: 3,
name: 'Ye',
},
];
const arr2 = [2, 3];
const newArr= arr1.filter(x => !arr2.some(y => y === x.key));
console.log(newArr);
Alternative syntax below :
const arr1 = [{
key: 1,
name: 'Al',
},
{
key: 2,
name: 'Lo',
},
{
key: 3,
name: 'Ye',
},
];
const arr2 = [2, 3];
const newArr = arr1.filter(({
key,
}) => !arr2.some(y => y === key));
console.log(newArr);
That said, you should be using Array.includes() like some ppl answered. It's simplier for the same outcome
const arr1 = [{
key: 1,
name: 'Al',
},
{
key: 2,
name: 'Lo',
},
{
key: 3,
name: 'Ye',
},
];
const arr2 = [2, 3];
const newArr = arr1.filter(({
key,
}) => !arr2.includes(key));
console.log(newArr);
You can do this
const newArr = arr1.filter(obj => !arr2.includes(obj.key));
This will work for you:
const arr1 = [
{
key: 1,
name: 'Al'
},
{
key: 2,
name: 'Lo'
},
{
key: 3,
name: 'Ye'
}
];
const arr2 = [2, 3]
const filtered = arr1.filter(val => !arr2.includes(val.key))
console.log(filtered)
:)
For situations like this Set is also very cool (and for big arrays more performant):
const arr1 = [
{
key: 1,
name: 'Al'
},
{
key: 2,
name: 'Lo'
},
{
key: 3,
name: 'Ye'
}
];
const arr2 = [2, 3]
const arr2Set = new Set(arr2);
const newArr = arr1.filter(obj1 => !arr2Set.has(obj1.key))
console.log(newArr)
You can use indexOf like this:
const newArr = arr1.filter(obj => arr2.indexOf(obj.key) > -1);
You need to filter the arr1 when arr1 element does not exist in arr2, so I think it could be better to use indexOf() like this
const newArr = arr1.filter(obj1 => arr2.indexOf(obj1.key) === -1)
if the element does not exist in arr2 it will return -1 which what you need.

Removing duplicate objects (based on multiple keys) from array

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);

Merging two arrays into one that has the same amount of objects

need help with little thing. I've searched current question, but didn't find good example.
I have two arrays with same count of objects:
array1 = [{ id: 1, name: 'Johnny'}, { id: 2, name: 'Mike'}];
array2 = [{ hair: 'black'}, { hair: 'white'}];
I need
array3 = [
{ id: 1, name: 'Johnny', hair: 'black'},
{ id: 2, name: 'Mike', hair: 'white'}
]
I've tried several ways with push and [...ES6], but I always return 4 objects instead of 2.
Use Object.assign of ES6:
var array1 = [{ id: 1, name: 'Johnny'}, { id: 2, name: 'Mike'}];
var array2 = [{ hair: 'black'}, { hair: 'white'}];
var merged = array1.map((el, index) => Object.assign(el, array2[index]));
console.log(merged);
You could use Array#reduce with Array#map and Object.assign for an arbitrary count of arrays and map objects at same index in one object.
var array1 = [{ id: 1, name: 'Johnny'}, { id: 2, name: 'Mike'}];
array2 = [{ hair: 'black'}, { hair: 'white'}],
result = [array1, array2]
.reduce((r, a) => a.map((o, i) => Object.assign(r[i] || {}, o), []));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories