I have two result sets like this:
// Result 1
[
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
{ value: "4", display: "Ryan" }
]
// Result 2
[
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
]
The final result I need is the difference between these arrays – the final result should be like this:
[{ value: "4", display: "Ryan" }]
Is it possible to do something like this in JavaScript?
This question is related to
javascript
arrays
object
I've made a generalized diff that compare 2 objects of any kind and can run a modification handler gist.github.com/bortunac "diff.js" an ex of using :
old_obj={a:1,b:2,c:[1,2]}
now_obj={a:2 , c:[1,3,5],d:55}
so property a is modified, b is deleted, c modified, d is added
var handler=function(type,pointer){
console.log(type,pointer,this.old.point(pointer)," | ",this.now.point(pointer));
}
now use like
df=new diff();
df.analize(now_obj,old_obj);
df.react(handler);
the console will show
mdf ["a"] 1 | 2
mdf ["c", "1"] 2 | 3
add ["c", "2"] undefined | 5
add ["d"] undefined | 55
del ["b"] 2 | undefined
If you are willing to use external libraries, You can use _.difference in underscore.js to achieve this. _.difference returns the values from array that are not present in the other arrays.
_.difference([1,2,3,4,5][1,4,10])
==>[2,3,5]
I take a slightly more general-purpose approach, although similar in ideas to the approaches of both @Cerbrus and @Kasper Moerch. I create a function that accepts a predicate to determine if two objects are equal (here we ignore the $$hashKey
property, but it could be anything) and return a function which calculates the symmetric difference of two lists based on that predicate:
a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]
b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]
var makeSymmDiffFunc = (function() {
var contains = function(pred, a, list) {
var idx = -1, len = list.length;
while (++idx < len) {if (pred(a, list[idx])) {return true;}}
return false;
};
var complement = function(pred, a, b) {
return a.filter(function(elem) {return !contains(pred, elem, b);});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
var myDiff = makeSymmDiffFunc(function(x, y) {
return x.value === y.value && x.display === y.display;
});
var result = myDiff(a, b); //=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}
It has one minor advantage over Cerebrus's approach (as does Kasper Moerch's approach) in that it escapes early; if it finds a match, it doesn't bother checking the rest of the list. If I had a curry
function handy, I would do this a little differently, but this works fine.
A comment asked for a more detailed explanation for beginners. Here's an attempt.
We pass the following function to makeSymmDiffFunc
:
function(x, y) {
return x.value === y.value && x.display === y.display;
}
This function is how we decide that two objects are equal. Like all functions that return true
or false
, it can be called a "predicate function", but that's just terminology. The main point is that makeSymmDiffFunc
is configured with a function that accepts two objects and returns true
if we consider them equal, false
if we don't.
Using that, makeSymmDiffFunc
(read "make symmetric difference function") returns us a new function:
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
This is the function we will actually use. We pass it two lists and it finds the elements in the first not in the second, then those in the second not in the first and combine these two lists.
Looking over it again, though, I could definitely have taken a cue from your code and simplified the main function quite a bit by using some
:
var makeSymmDiffFunc = (function() {
var complement = function(pred, a, b) {
return a.filter(function(x) {
return !b.some(function(y) {return pred(x, y);});
});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
complement
uses the predicate and returns the elements of its first list not in its second. This is simpler than my first pass with a separate contains
function.
Finally, the main function is wrapped in an immediately invoked function expression (IIFE) to keep the internal complement
function out of the global scope.
Update, a few years later
Now that ES2015 has become pretty well ubiquitous, I would suggest the same technique, with a lot less boilerplate:
const diffBy = (pred) => (a, b) => a.filter(x => !b.some(y => pred(x, y)))
const makeSymmDiffFunc = (pred) => (a, b) => diffBy(pred)(a, b).concat(diffBy(pred)(b, a))
const myDiff = makeSymmDiffFunc((x, y) => x.value === y.value && x.display === y.display)
const result = myDiff(a, b)
//=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}
I think the @Cerbrus solution is spot on. I have implemented the same solution but extracted the repeated code into it's own function (DRY).
function filterByDifference(array1, array2, compareField) {
var onlyInA = differenceInFirstArray(array1, array2, compareField);
var onlyInb = differenceInFirstArray(array2, array1, compareField);
return onlyInA.concat(onlyInb);
}
function differenceInFirstArray(array1, array2, compareField) {
return array1.filter(function (current) {
return array2.filter(function (current_b) {
return current_b[compareField] === current[compareField];
}).length == 0;
});
}
Most generic and simple way:
findObject(listOfObjects, objectToSearch) {
let found = false, matchingKeys = 0;
for(let object of listOfObjects) {
found = false;
matchingKeys = 0;
for(let key of Object.keys(object)) {
if(object[key]==objectToSearch[key]) matchingKeys++;
}
if(matchingKeys==Object.keys(object).length) {
found = true;
break;
}
}
return found;
}
get_removed_list_of_objects(old_array, new_array) {
// console.log('old:',old_array);
// console.log('new:',new_array);
let foundList = [];
for(let object of old_array) {
if(!this.findObject(new_array, object)) foundList.push(object);
}
return foundList;
}
get_added_list_of_objects(old_array, new_array) {
let foundList = [];
for(let object of new_array) {
if(!this.findObject(old_array, object)) foundList.push(object);
}
return foundList;
}
import differenceBy from 'lodash/differenceBy'
const myDifferences = differenceBy(Result1, Result2, 'value')
This will return the difference between two arrays of objects, using the key value
to compare them. Note two things with the same value will not be returned, as the other keys are ignored.
This is a part of lodash.
You can create an object with keys as the unique value corresponding for each object in array and then filter each array based on existence of the key in other's object. It reduces the complexity of the operation.
ES6
let a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}];_x000D_
let b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}];_x000D_
_x000D_
let valuesA = a.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});_x000D_
let valuesB = b.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});_x000D_
let result = [...a.filter(({value}) => !valuesB[value]), ...b.filter(({value}) => !valuesA[value])];_x000D_
console.log(result);
_x000D_
ES5
var a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}];_x000D_
var b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}];_x000D_
_x000D_
var valuesA = a.reduce(function(a,c){a[c.value] = c.value; return a; }, {});_x000D_
var valuesB = b.reduce(function(a,c){a[c.value] = c.value; return a; }, {});_x000D_
var result = a.filter(function(c){ return !valuesB[c.value]}).concat(b.filter(function(c){ return !valuesA[c.value]}));_x000D_
console.log(result);
_x000D_
Most of answers here are rather complex, but isn't logic behind this quite simple?
const getArraysDifference = (longerArray, array2) => {
const difference = [];
longerArray.forEach(el1 => { /*1*/
el1IsPresentInArr2 = array2.some(el2 => el2.value === el1.value); /*2*/
if (!el1IsPresentInArr2) { /*3*/
difference.push(el1); /*4*/
}
});
return difference;
}
O(n^2) complexity.
In addition, say two object array with different key value
// Array Object 1
const arrayObjOne = [
{ userId: "1", display: "Jamsheer" },
{ userId: "2", display: "Muhammed" },
{ userId: "3", display: "Ravi" },
{ userId: "4", display: "Ajmal" },
{ userId: "5", display: "Ryan" }
]
// Array Object 2
const arrayObjTwo =[
{ empId: "1", display: "Jamsheer", designation:"Jr. Officer" },
{ empId: "2", display: "Muhammed", designation:"Jr. Officer" },
{ empId: "3", display: "Ravi", designation:"Sr. Officer" },
{ empId: "4", display: "Ajmal", designation:"Ast. Manager" },
]
You can use filter
in es5
or native js
to substract two array object.
//Find data that are in arrayObjOne but not in arrayObjTwo
var uniqueResultArrayObjOne = arrayObjOne.filter(function(objOne) {
return !arrayObjTwo.some(function(objTwo) {
return objOne.userId == objTwo.empId;
});
});
In ES6
you can use Arrow function with Object destructuring of ES6
.
const ResultArrayObjOne = arrayObjOne.filter(({ userId: userId }) => !arrayObjTwo.some(({ empId: empId }) => empId === userId));
console.log(ResultArrayObjOne);
JavaScript has Maps, that provide O(1) insertion and lookup time. Therefore this can be solved in O(n) (and not O(n²) as all the other answers do). For that, it is necessary to generate a unique primitive (string / number) key for each object. One could JSON.stringify
, but that's quite error prone as the order of elements could influence equality:
JSON.stringify({ a: 1, b: 2 }) !== JSON.stringify({ b: 2, a: 1 })
Therefore, I'd take a delimiter that does not appear in any of the values and compose a string manually:
const toHash = value => value.value + "@" + value.display;
Then a Map gets created. When an element exists already in the Map, it gets removed, otherwise it gets added. Therefore only the elements that are included odd times (meaning only once) remain. This will only work if the elements are unique in each array:
const entries = new Map();
for(const el of [...firstArray, ...secondArray]) {
const key = toHash(el);
if(entries.has(key)) {
entries.delete(key);
} else {
entries.set(key, el);
}
}
const result = [...entries.values()];
const firstArray = [_x000D_
{ value: "0", display: "Jamsheer" },_x000D_
{ value: "1", display: "Muhammed" },_x000D_
{ value: "2", display: "Ravi" },_x000D_
{ value: "3", display: "Ajmal" },_x000D_
{ value: "4", display: "Ryan" }_x000D_
]_x000D_
_x000D_
const secondArray = [_x000D_
{ value: "0", display: "Jamsheer" },_x000D_
{ value: "1", display: "Muhammed" },_x000D_
{ value: "2", display: "Ravi" },_x000D_
{ value: "3", display: "Ajmal" },_x000D_
];_x000D_
_x000D_
const toHash = value => value.value + "@" + value.display;_x000D_
_x000D_
const entries = new Map();_x000D_
_x000D_
for(const el of [...firstArray, ...secondArray]) {_x000D_
const key = toHash(el);_x000D_
if(entries.has(key)) {_x000D_
entries.delete(key);_x000D_
} else {_x000D_
entries.set(key, el);_x000D_
}_x000D_
}_x000D_
_x000D_
const result = [...entries.values()];_x000D_
_x000D_
console.log(result);
_x000D_
let obj1 =[
{ id: 1, submenu_name: 'login' },
{ id: 2, submenu_name: 'Profile',},
{ id: 3, submenu_name: 'password', },
{ id: 4, submenu_name: 'reset',}
] ;
let obj2 =[
{ id: 2},
{ id: 3 },
] ;
// Need Similar obj
const result1 = obj1.filter(function(o1){
return obj2.some(function(o2){
return o1.id == o2.id; // id is unnique both array object
});
});
console.log(result1);
// Need differnt obj
const result2 = obj1.filter(function(o1){
return !obj2.some(function(o2){ // for diffrent we use NOT (!) befor obj2 here
return o1.id == o2.id; // id is unnique both array object
});
});
console.log(result2);
_x000D_
You could use Array.prototype.filter()
in combination with Array.prototype.some()
.
Here is an example (assuming your arrays are stored in the variables result1
and result2
):
//Find values that are in result1 but not in result2
var uniqueResultOne = result1.filter(function(obj) {
return !result2.some(function(obj2) {
return obj.value == obj2.value;
});
});
//Find values that are in result2 but not in result1
var uniqueResultTwo = result2.filter(function(obj) {
return !result1.some(function(obj2) {
return obj.value == obj2.value;
});
});
//Combine the two arrays of unique entries
var result = uniqueResultOne.concat(uniqueResultTwo);
you can do diff a on b and diff b on a, then merge both results
let a = [_x000D_
{ value: "0", display: "Jamsheer" },_x000D_
{ value: "1", display: "Muhammed" },_x000D_
{ value: "2", display: "Ravi" },_x000D_
{ value: "3", display: "Ajmal" },_x000D_
{ value: "4", display: "Ryan" }_x000D_
]_x000D_
_x000D_
let b = [_x000D_
{ value: "0", display: "Jamsheer" },_x000D_
{ value: "1", display: "Muhammed" },_x000D_
{ value: "2", display: "Ravi" },_x000D_
{ value: "3", display: "Ajmal" }_x000D_
]_x000D_
_x000D_
// b diff a_x000D_
let resultA = b.filter(elm => !a.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm)));_x000D_
_x000D_
// a diff b_x000D_
let resultB = a.filter(elm => !b.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm))); _x000D_
_x000D_
// show merge _x000D_
console.log([...resultA, ...resultB]);
_x000D_
I came across this question while searching for a way to pick out the first item in one array that does not match any of the values in another array and managed to sort it out eventually with array.find() and array.filter() like this
var carList= ['mercedes', 'lamborghini', 'bmw', 'honda', 'chrysler'];
var declinedOptions = ['mercedes', 'lamborghini'];
const nextOption = carList.find(car=>{
const duplicate = declinedOptions.filter(declined=> {
return declined === car
})
console.log('duplicate:',duplicate) //should list out each declined option
if(duplicate.length === 0){//if theres no duplicate, thats the nextOption
return car
}
})
console.log('nextOption:', nextOption);
//expected outputs
//duplicate: mercedes
//duplicate: lamborghini
//duplicate: []
//nextOption: bmw
if you need to keep fetching an updated list before cross-checking for the next best option this should work well enough :)
I found this solution using filter and some.
resultFilter = (firstArray, secondArray) => {_x000D_
return firstArray.filter(firstArrayItem =>_x000D_
!secondArray.some(_x000D_
secondArrayItem => firstArrayItem._user === secondArrayItem._user_x000D_
)_x000D_
);_x000D_
};
_x000D_
I prefer map object when it comes to big arrays.
// create tow arrays_x000D_
array1 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))_x000D_
array2 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))_x000D_
_x000D_
// calc diff with some function_x000D_
console.time('diff with some');_x000D_
results = array2.filter(({ value: id1 }) => array1.some(({ value: id2 }) => id2 === id1));_x000D_
console.log('diff results ',results.length)_x000D_
console.timeEnd('diff with some');_x000D_
_x000D_
// calc diff with map object_x000D_
console.time('diff with map');_x000D_
array1Map = {};_x000D_
for(const item1 of array1){_x000D_
array1Map[item1.value] = true;_x000D_
}_x000D_
results = array2.filter(({ value: id2 }) => array1Map[id2]);_x000D_
console.log('map results ',results.length)_x000D_
console.timeEnd('diff with map');
_x000D_
For those who like one-liner solutions in ES6, something like this:
const arrayOne = [ _x000D_
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer" },_x000D_
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed" },_x000D_
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi" },_x000D_
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal" },_x000D_
{ value: "a63a6f77-c637-454e-abf2-dfb9b543af6c", display: "Ryan" },_x000D_
];_x000D_
_x000D_
const arrayTwo = [_x000D_
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer"},_x000D_
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed"},_x000D_
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi"},_x000D_
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal"},_x000D_
];_x000D_
_x000D_
const results = arrayOne.filter(({ value: id1 }) => !arrayTwo.some(({ value: id2 }) => id2 === id1));_x000D_
_x000D_
console.log(results);
_x000D_
Source: Stackoverflow.com