How to get unique element from two arrays in js? - javascript

I'm trying to get unique (by id) values from two arrays.
But it returns whole array instead of { id: 3 }
const a = [{ id: 1 }, { id: 2 }];
const b = [{ id: 1 }, { id: 2 }, { id: 3 }];
const array3 = b.filter((obj) => a.indexOf(obj) == -1);
console.log(array3);
What's wrong here?

You cannot compare objects you should check that an element with that id doesn't exists in the other array
here I used some that returns a boolean if he can find a match
const a = [{
id: 1
}, {
id: 2
}];
const b = [{
id: 1
}, {
id: 2
}, {
id: 3
}];
const array3 = b.filter(obj => !a.some(({id}) => obj.id === id));
console.log(array3)

In your case, the following code gives all unique objects as an array, based on the id.
const a = [{
id: 1
}, {
id: 2
}];
const b = [{
id: 1
}, {
id: 2
}, {
id: 3
}];
const array3 = b.filter(objB => a.some((objA) => objB.id !== objA.id));
console.log(array3)

A different approach with a symmetrically result.
const
take = m => d => o => m.set(o.id, (m.get(o.id) || 0) + d),
a = [{ id: 1 }, { id: 2 }],
b = [{ id: 1 }, { id: 2 }, { id: 3 }],
map = new Map(),
add = take(map),
result = [];
a.forEach(add(1));
b.forEach(add(-1));
map.forEach((v, id) => v && result.push({ id }));
console.log(result);

Related

Compare two arrays of objects, and remove if object value is equal

I've tried modifying some of the similar solutions on here but I keep getting stuck, I believe I have part of this figured out however, the main caveat is that:
Some of the objects have extra keys, which renders my object comparison logic useless.
I am trying to compare two arrays of objects. One array is the original array, and the other array contains the items I want deleted from the original array. However there's one extra issue in that the second array contains extra keys, so my comparison logic doesn't work.
An example would make this easier, let's say I have the following two arrays:
const originalArray = [{id: 1, name: "darnell"}, {id: 2, name: "funboi"},
{id: 3, name: "jackson5"}, {id: 4, name: "zelensky"}];
const itemsToBeRemoved = [{id: 2, name: "funboi", extraProperty: "something"},
{id: 4, name: "zelensky", extraProperty: "somethingelse"}];
after running the logic, my final output should be this array:
[{id: 1, name: "darnell"}, {id: 3, name: "jackson5"}]
And here's the current code / logic that I have, which compares but doesn't handle the extra keys. How should I handle this? Thank you in advance.
const prepareArray = (arr) => {
return arr.map((el) => {
if (typeof el === "object" && el !== null) {
return JSON.stringify(el);
} else {
return el;
}
});
};
const convertJSON = (arr) => {
return arr.map((el) => {
return JSON.parse(el);
});
};
const compareArrays = (arr1, arr2) => {
const currentArray = [...prepareArray(arr1)];
const deletedItems = [...prepareArray(arr2)];
const compared = currentArray.filter((el) => deletedItems.indexOf(el) === -1);
return convertJSON(compared);
};
How about using filter and some? You can extend the filter condition on select properties using &&.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
console.log(
originalArray.filter(item => !itemsToBeRemoved.some(itemToBeRemoved => itemToBeRemoved.id === item.id))
)
Or you can generalise it as well.
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
function filterIfSubset(originalArray, itemsToBeRemoved) {
const filteredArray = [];
for (let i = 0; i < originalArray.length; i++) {
let isSubset = false;
for (let j = 0; j < itemsToBeRemoved.length; j++) {
// check if whole object is a subset of the object in itemsToBeRemoved
if (Object.keys(originalArray[i]).every(key => originalArray[i][key] === itemsToBeRemoved[j][key])) {
isSubset = true;
}
}
if (!isSubset) {
filteredArray.push(originalArray[i]);
}
}
return filteredArray;
}
console.log(filterIfSubset(originalArray, itemsToBeRemoved));
Another simpler variation of the second approach:
const originalArray = [
{ id: 1, name: 'darnell' },
{ id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' },
];
const itemsToBeRemoved = [
{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' },
];
const removeSubsetObjectsIfExists = (originalArray, itemsToBeRemoved) => {
return originalArray.filter(item => {
const isSubset = itemsToBeRemoved.some(itemToBeRemoved => {
return Object.keys(item).every(key => {
return item[key] === itemToBeRemoved[key];
});
});
return !isSubset;
});
}
console.log(removeSubsetObjectsIfExists(originalArray, itemsToBeRemoved));
The example below is a reusable function, the third parameter is the key to which you compare values from both arrays.
Details are commented in example
const arr=[{id:1,name:"darnell"},{id:2,name:"funboi"},{id:3,name:"jackson5"},{id:4,name:"zelensky"}],del=[{id:2,name:"funboi",extraProperty:"something"},{id:4,name:"zelensky",extraProperty:"somethingelse"}];
/** Compare arrayA vs. delArray by a given key's value.
--- ex. key = 'id'
**/
function deleteByKey(arrayA, delArray, key) {
/* Get an array of only the values of the given key from delArray
--- ex. delList = [1, 2, 3, 4]
*/
const delList = delArray.map(obj => obj[key]);
/* On every object of arrayA compare delList values vs
current object's key's value
--- ex. current obj[id] = 2
--- [1, 2, 3, 4].includes(obj[id])
Any match returns an empty array and non-matches are returned
in it's own array.
--- ex. ? [] : [obj]
The final return is a flattened array of the non-matching objects
*/
return arrayA.flatMap(obj => delList.includes(obj[key]) ? [] : [obj]);
};
console.log(deleteByKey(arr, del, 'id'));
let ff = [{ id: 1, name: 'darnell' }, { id: 2, name: 'funboi' },
{ id: 3, name: 'jackson5' },
{ id: 4, name: 'zelensky' }]
let cc = [{ id: 2, name: 'funboi', extraProperty: 'something' },
{ id: 4, name: 'zelensky', extraProperty: 'somethingelse' }]
let ar = []
let out = []
const result = ff.filter(function(i){
ar.push(i.id)
cc.forEach(function(k){
out.push(k.id)
})
if(!out.includes(i.id)){
// console.log(i.id, i)
return i
}
})
console.log(result)

What happen when i run array.some in array filter

I just learned a new trick to find the same object in 2 array object, it works very well. it uses array.filter and array.some, as code bellow, but I don't understand how filter() can run when some() will return true or false.
const similarity = (arr, values) => arr.filter(item => values.some(m => (m.id === item.id) && (m.name === item.name)));
my input:
let arr1 = [
{
id: 1,
name: "kiet"
},
{
id: 2,
name: 'phan'
},
{
id: 3,
name: 'tuan'
}]
let arr2 = [
{
id: 1,
name: "kiet"
},
{
id: 2,
name: 'haha'
},
{
id: 5,
name: 'tuan'
}
]
my result :
[ { id: 1, name: 'kiet' } ]
You are Filtering the array, then passing this condition inside the some
(m.id === item.id) && (m.name === item.name)
If the id of the second array (here, m is the second array's objects) is equal to the first array's id (here, item is the array's objects) and the name is equal to the first array's name property. If yes then it will return true. So the filter gets a true and if is does, it will return that particular object for which it gets a true
let arr1 = [{ id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan'}]
let arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' } ]
const similarity = (arr, values) => arr.filter(item => values.some(m => (m.id === item.id) && (m.name === item.name)));
console.log(similarity(arr1,arr2))
You get the elements from the array which have the same properties id/name as the values array.
Array#filter needs a (kind of) boolean value and if true or truthy, then the item is taken to the new array.
Array#some checks an item and if the callback returns a truthy value, then it returns true, if not then false.
const
similarity = (array, values) => array.filter(item => values.some(m => m.id === item.id && m.name === item.name)),
arr1 = [ { id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan' }],
arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' }],
result = similarity(arr1, arr2);
console.log(result);
For larger data sets, you could take a Set, this is iterated once with a combined value and checked against for filtering.
const
similarity = (array, values) => {
const
getKey = ({ id, name }) => [id, name].join('|'),
keys = new Set(values.map(getKey));
return array.filter(o => keys.has(getKey(o)));
},
arr1 = [ { id: 1, name: "kiet" }, { id: 2, name: 'phan' }, { id: 3, name: 'tuan' }],
arr2 = [ { id: 1, name: "kiet" }, { id: 2, name: 'haha' }, { id: 5, name: 'tuan' }],
result = similarity(arr1, arr2);
console.log(result);
This is a good technique if you want to check for array intersection.
const vowels = [...'AEIOU'];
const isVowel = x => vowels.some(v => v === x);
const hasVowel = string => [...string].filter(isVowel).length > 0;

If you want to remove an object from the first array which is not present in second array

If you want to remove more than one object from the first array "arrayOne" which is not present in the second array "arrayTwo". It's just a suggestion the way I do. If you have any other way please let me know.
let arrayOne = [{
id: 1
}, {
id: 2
}, {
id: 3
}]
let arrayTwo = [{
id: 2
},{
id: 3
}]
for (var index = arrayOne.length; index--;) {
if (!arrayTwo.find(y => y.id === arrayOne[index].id)) {
arrayOne.splice(arrayOne.findIndex(z => z.id === arrayOne[index].id), 1)
console.log("After splice", arrayOne)
}
You can also use a Set to store ids of elements of arrayTwo and then filter to extract only those elements of arrayOne that are also present in arrayTwo:
let arrayOne = [{
id: 1
}, {
id: 2
}, {
id: 3
}];
let arrayTwo = [{
id: 2
}];
let arrayTwoSet = new Set(arrayTwo.map(e => e.id));
console.log(arrayOne.filter(e => arrayTwoSet.has(e.id)));
use Array.some() inside Array.filter()
let arrayOne = [{ id: 1 }, { id: 2 }, { id: 3 }] ;
let arrayTwo = [{ id: 2 }];
const result = arrayOne.filter(obj1 => !arrayTwo.some(obj2 => obj1.id === obj2.id));
console.log('final array : ', result);

JavaScript - Filter a array by iterating on another array

The first object{array}, the one i want to filter :
const object1 = {
"count" : 2,
"result" : [
{ "id": 1 },
{ "id": 2 }
]
}
The second array :
const array2 = [{
"id": 1,
"id": 44
}]
I want to filter the first array object1.result (or create a new array apart from it) if object.result[i].id is equal to array2[i].id , an reduce the number of array count object1.count based on the number of filter element.
in the example above I should have a new object :
theNewObject = {
"count" : 1,
"result" : [
{ "id": 2 }
]
}
You could use a Set an collect all id for filtering.
var object = { count : 2, result : [{ id: 1 }, { id: 2 }] },
array = [{ id: 1 }, { id: 44 }],
ids = new Set(array.map(({ id }) => id));
object.result = object.result.filter(({ id }) => ids.has(id));
object.count = object.result.length;
console.log(object);
An approach which counts down the count.
var object = { count : 2, result : [{ id: 1 }, { id: 2 }] },
array = [{ id: 1 }, { id: 44 }],
ids = new Set(array.map(({ id }) => id));
object.result = object.result.filter(({ id }) => ids.has(id) || !object.count--);
console.log(object);
You can combine the filter() method with the find() method:
const object1 = {
count: 2,
result: [
{ id: 1 },
{ id: 2 },
],
};
const array2 = [
{ id: 1 },
{ id: 4 },
];
const filteredResult = object1.result.filter(({ id }) => !array2.find(x => x.id === id));
const object3 = {
count: filteredResult.length,
result: filteredResult,
};
console.log(object3);
The { id } syntax is destructuring assignment.
You can also use reduce():
const object1 = {
count: 2,
result: [
{ id: 1 },
{ id: 2 },
],
};
const array2 = [
{ id: 1 },
{ id: 4 },
];
const object3 = object1.result.reduce(
({ count, result }, { id }) => array2.find(x => x.id === id)
? ({ count, result })
: ({ count: count + 1, result: result.concat([{ id }]) }),
{ count: 0, result: [] },
);
console.log(object3);

Compare two Arrays with Objects and create new array with unmatched objects

I have the following two Javascript arrays:
const array1 = [{ id: 1}, { id: 2 }, { id: 3 }, { id: 4}];
const array2 = [{ id: 1}, { id: 3 }];
I now want a new array array3 that contains only the objects that aren't already in array2, so:
const array3 = [{ id: 2}, { id: 4 }];
I have tried the following but it returns all objects, and when I changed the condition to === it returns the objects of array2.
const array3 = array1.filter(entry1 => {
return array2.some(entry2 => entry1.id !== entry2.id);
});
Any idea? ES6 welcome
You could reverse the comparison (equal instead of unqual) and return the negated result of some.
const
array1 = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }],
array2 = [{ id: 1 }, { id: 3 }],
array3 = array1.filter(entry1 => !array2.some(entry2 => entry1.id === entry2.id));
// ^ ^^^
console.log(array3);
Nina's answer is a good start but will miss any unique elements in array 2.
This extends her answer to get the unique elements from each array and then combine them:
const
array1 = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }],
array2 = [{ id: 1 }, { id: 3 }, { id: 5 }],
array3 = array1.filter(entry1 => !array2.some(entry2 => entry1.id === entry2.id)),
array4 = array2.filter(entry1 => !array1.some(entry2 => entry1.id === entry2.id)),
array5 = array3.concat(array4);
console.log(array5);

Categories