Reputation: 14157
Is there a way to return the difference between two arrays in JavaScript?
For example:
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
// need ["c", "d"]
Upvotes: 1396
Views: 1243968
Reputation: 31612
Array.prototype.filter()
and Array.prototype.includes()
come in handy here.
let intersection = arr1.filter(x => arr2.includes(x));
Yields values which are present in both A
and B
.
let A, B, intersection;
A = [1, 2, 3];
B = [2, 3];
intersection = A.filter(x => B.includes(x));
console.log(intersection);
A = [1, 2, 3];
B = [2, 3, 5];
intersection = A.filter(x => B.includes(x));
console.log(intersection);
// both operations return [2, 3]
(Values in just A.)
let difference = arr1.filter(x => !arr2.includes(x));
Yields values that are present in just A
.
let A, B, intersection;
A = [1, 2, 3];
B = [2, 3];
intersection = A.filter(x => !B.includes(x));
console.log(intersection);
A = [1, 2, 3];
B = [2, 3, 5];
intersection = A.filter(x => !B.includes(x));
console.log(intersection);
// both operations return [1]
let symDifference = arr1.filter(x => !arr2.includes(x))
.concat(arr2.filter(x => !arr1.includes(x)));
Yields values that are only in A
or B
, but not both ("exclusive or").
This is what you get if you take the difference of both arrays with each other, and combine the two results (You get an array containing all the elements of A
that are not in B
and vice-versa).
let A, B, intersection;
A = [1, 2, 3];
B = [2, 3];
intersection = A.filter(x => !B.includes(x))
.concat(B.filter(x => !A.includes(x)));
console.log(intersection);
// returns [1]
A = [1, 2, 3];
B = [2, 3, 5];
intersection = A.filter(x => !B.includes(x))
.concat(B.filter(x => !A.includes(x)));
console.log(intersection);
// returns [1, 5]
See the answer by @Joshaven Potter, for an example of how you can use these functions directly on arrays.
Upvotes: 3006
Reputation: 188
const a1 = ['a', 'b', 'c', 'd'];
const a2 = ['a', 'b'];
const diffArr = a1.filter(o => !a2.includes(o));
console.log(diffArr);
Output:
[ 'c', 'd' ]
Upvotes: 6
Reputation: 473
Here is the faster approach by using map
const arr1 = ['a','b','c','d'];
const arr2 = ['a','b','c']
let table = {}
arr1.forEach(v=>{table[v]=false})
arr2.forEach(v=>{
if(table[v]===false) table[v] = true
else table[v] = false
})
const diff = Object.keys(table).filter(v=>table[v]==false)
Upvotes: 0
Reputation: 87
const dbData = [{name:'ally'},
{name:'James'}]
const localData = [{name:'James'}]
const diff = dbData.filter(a =>!localData.some(b => { return a.name === b.name}))
Upvotes: 3
Reputation: 399
Here is another solution that can return the differences, just like git diff: (it has been written in typescript, if you're not using typescript version, just remove the types)
/**
* util function to calculate the difference between two arrays (pay attention to 'from' and 'to'),
* it would return the mutations from 'from' to 'to'
* @param { T[] } from
* @param { T[] } to
* @returns { { [x in string]: boolean } } it would return the stringified version of array element, true means added,
* false means removed
*/
export function arrDiff<T>(from: T[], to: T[]): { [x in string]: boolean } {
var diff: { [x in string]: boolean } = {};
var newItems: T[] = []
diff = from.reduce((a, e) => ({ ...a, [JSON.stringify(e)]: true }), {})
for (var i = 0; i < to.length; i++) {
if (diff[JSON.stringify(to[i])]) {
delete diff[JSON.stringify(to[i])]
} else {
newItems.push(to[i])
}
}
return {
...Object.keys(diff).reduce((a, e) => ({ ...a, [e]: false }), {}),
...newItems.reduce((a, e) => ({ ...a, [JSON.stringify(e)]: true }), {})
}
}
Here is a sample of usage:
arrDiff(['a', 'b', 'c'], ['a', 'd', 'c', 'f']) //{"b": false, "d": true, "f": true}
Upvotes: 5
Reputation: 461
function diffArray(arr1, arr2) {
const newArr = [];
// arr1 match to arr2
arr1.map((item)=>{
if(arr2.indexOf(item)<0){
console.log(item)
newArr.push(item)
}
})
// arr2 match to arr1
arr2.map((item)=>{
if(arr1.indexOf(item)<0){
newArr.push(item)
}
})
return newArr;
}
diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5])
Output :: [ 4 ]
Upvotes: 0
Reputation: 13
function array_diff(array1, array2) {
let returnArray = [];
$.each(array1, function(index, value) {
let findStatus = false;
if (Array.isArray(array2)) {
$.each(array2, function(index2, value2) {
if (value == value2) findStatus = true;
});
} else {
if (value == array2) {
findStatus = true;
}
}
if (findStatus == false) {
returnArray.push(value);
}
});
return returnArray;
}
Upvotes: 0
Reputation: 5738
const unique = (a) => [...new Set(a)];
const uniqueBy = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));
const intersection = (a, b) => a.filter((v) => b.includes(v));
const diff = (a, b) => a.filter((v) => !b.includes(v));
const symDiff = (a, b) => diff(a, b).concat(diff(b, a));
const union = (a, b) => diff(a, b).concat(b);
const a = unique([1, 2, 3, 4, 5, 5]);
console.log(a);
const b = [4, 5, 6, 7, 8];
console.log(intersection(a, b), diff(a, b), symDiff(a, b), union(a, b));
console.log(uniqueBy(
[
{ id: 1, name: "abc" },
{ id: 2, name: "xyz" },
{ id: 1, name: "abc" },
],
(v) => v.id
));
const intersectionBy = (a, b, f) => a.filter((v) => b.some((u) => f(v, u)));
console.log(intersectionBy(
[
{ id: 1, name: "abc" },
{ id: 2, name: "xyz" },
],
[
{ id: 1, name: "abc" },
{ id: 3, name: "pqr" },
],
(v, u) => v.id === u.id
));
const diffBy = (a, b, f) => a.filter((v) => !b.some((u) => f(v, u)));
console.log(diffBy(
[
{ id: 1, name: "abc" },
{ id: 2, name: "xyz" },
],
[
{ id: 1, name: "abc" },
{ id: 3, name: "pqr" },
],
(v, u) => v.id === u.id
));
const unique = <T>(array: T[]) => [...new Set(array)];
const intersection = <T>(array1: T[], array2: T[]) =>
array1.filter((v) => array2.includes(v));
const diff = <T>(array1: T[], array2: T[]) =>
array1.filter((v) => !array2.includes(v));
const symDiff = <T>(array1: T[], array2: T[]) =>
diff(array1, array2).concat(diff(array2, array1));
const union = <T>(array1: T[], array2: T[]) =>
diff(array1, array2).concat(array2);
const intersectionBy = <T>(
array1: T[],
array2: T[],
predicate: (array1Value: T, array2Value: T) => boolean
) => array1.filter((v) => array2.some((u) => predicate(v, u)));
const diffBy = <T>(
array1: T[],
array2: T[],
predicate: (array1Value: T, array2Value: T) => boolean
) => array1.filter((v) => !array2.some((u) => predicate(v, u)));
const uniqueBy = <T>(
array: T[],
predicate: (v: T, i: number, a: T[]) => string
) =>
Object.values(
array.reduce((acc, value, index) => {
acc[predicate(value, index, array)] = value;
return acc;
}, {} as { [key: string]: T })
);
Upvotes: 44
Reputation: 47
try it.
var first = [ 1, 2, 3, 4, 5 ];
var second = [ 4, 5, 6 ];
var difference = first.filter(x => second.indexOf(x) === -1);
console.log(difference);
Output: [ 1, 2, 3]
var first = [ 1, 2, 3, 4, 5 ];
var second = [ 4, 5, 6 ];
var difference = first.filter(x => second.indexOf(x) === -1);
console.log(difference);
Upvotes: 3
Reputation: 1343
If you have two list of objects
const people = [{name: 'cesar', age: 23}]
const morePeople = [{name: 'cesar', age: 23}, {name: 'kevin', age: 26}, {name: 'pedro', age: 25}]
let result2 = morePeople.filter(person => people.every(person2 => !person2.name.includes(person.name)))
Upvotes: 8
Reputation: 66
In response to post (Comparing two arrays containing integers JavaScript) by adaen that was closed:
A couple of options:
const arrOne = [2,3,10,7,9,15,7,15,21,1];
const arrTwo = [3,15,1,2,21];
const hash = {};
arrTwo.forEach(a => hash[a]++);
arrOne.filter(a => typeof hash[a] === 'undefined').forEach(a => console.log(a));
const arrOne = [2,3,10,7,9,15,7,15,21,1].sort((a,b)=>a-b);
const arrTwo = [3,15,1,2,21].sort((a,b)=>a-b);
var i1 = 0;
for(var i2 = 0; i2 < arrTwo.length; i2++) {
while(arrOne[i1] < arrTwo[i2+1]) {
if(arrOne[i1] != arrTwo[i2]) {
console.log(arrOne[i1]);
}
i1++;
}
}
Upvotes: 0
Reputation: 14464
This answer was written in 2009, so it is a bit outdated, also it's rather educational for understanding the problem. Best solution I'd use today would be
let difference = arr1.filter(x => !arr2.includes(x));
(credits to other author here)
I assume you are comparing a normal array. If not, you need to change the for loop to a for .. in loop.
function arr_diff (a1, a2) {
var a = [], diff = [];
for (var i = 0; i < a1.length; i++) {
a[a1[i]] = true;
}
for (var i = 0; i < a2.length; i++) {
if (a[a2[i]]) {
delete a[a2[i]];
} else {
a[a2[i]] = true;
}
}
for (var k in a) {
diff.push(k);
}
return diff;
}
console.log(arr_diff(['a', 'b'], ['a', 'b', 'c', 'd']));
console.log(arr_diff("abcd", "abcde"));
console.log(arr_diff("zxc", "zxc"));
Upvotes: 387
Reputation: 4577
If you want to find the difference between two arrays of object you can do it like this :
let arrObj = [{id: 1},{id: 2},{id: 3}]
let arrObj2 = [{id: 1},{id: 3}]
let result = arrObj.filter(x => arrObj2.every(x2 => x2.id !== x.id))
console.log(result)
Upvotes: 3
Reputation: 10356
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
//////////////
// Examples //
//////////////
const dif1 = [1,2,3,4,5,6].diff( [3,4,5] );
console.log(dif1); // => [1, 2, 6]
const dif2 = ["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]);
console.log(dif2); // => ["test5", "test6"]
Note .indexOf()
and .filter()
are not available before IE9.
Upvotes: 960
Reputation: 3252
To find the difference of 2 arrays without duplicates:
function difference(arr1, arr2){
let setA = new Set(arr1);
let differenceSet = new Set(arr2.filter(ele => !setA.has(ele)));
return [...differenceSet ];
}
1.difference([2,2,3,4],[2,3,3,4])
will return []
2.difference([1,2,3],[4,5,6])
will return [4,5,6]
3.difference([1,2,3,4],[1,2])
will return []
4.difference([1,2],[1,2,3,4])
will return [3,4]
Note: The above solution requires that you always send the larger array as the second parameter. To find the absolute difference, you will need to first find the larger array of the two and then work on them.
To find the absolute difference of 2 arrays without duplicates:
function absDifference(arr1, arr2){
const {larger, smaller} = arr1.length > arr2.length ?
{larger: arr1, smaller: arr2} : {larger: arr2, smaller: arr1}
let setA = new Set(smaller);
let absDifferenceSet = new Set(larger.filter(ele => !setA.has(ele)));
return [...absDifferenceSet ];
}
1.absDifference([2,2,3,4],[2,3,3,4])
will return []
2.absDifference([1,2,3],[4,5,6])
will return [4,5,6]
3.absDifference([1,2,3,4],[1,2])
will return [3,4]
4.absDifference([1,2],[1,2,3,4])
will return [3,4]
Note the example 3 from both the solutions
Upvotes: 5
Reputation: 74
function arrayDiff(a, b) {
return a.concat(b).filter(val => !(b.includes(val)));
//(or) return a.concat(b).filter(val => !(a.includes(val) && b.includes(val)));
}
Upvotes: 1
Reputation: 2501
function diffArray(newArr, oldArr) {
var newSet = new Set(newArr)
var diff = []
oldArr.forEach((a) => {
if(!newSet.delete(a))diff.push(a)
})
return diff.concat(Array.from(newSet))
}
Upvotes: 0
Reputation: 222841
With the arrival of ES6 with sets and splat operator (at the time of being works only in Firefox, check compatibility table), you can write the following one liner:
var a = ['a', 'b', 'c', 'd'];
var b = ['a', 'b'];
var b1 = new Set(b);
var difference = [...new Set(a.filter(x => !b1.has(x)))];
which will result in [ "c", "d" ]
.
Upvotes: 34
Reputation: 826
var compare = array1.length > array2.length ? array1 : array2;
var compareWith = array1.length > array2.length ? array2 : array1;
var uniqueValues = compareWith.filter(function(value){
if(compare.indexOf(vakye) == -1)
return true;
});
This will both check which one is the larger one among the arrays and then will do the comparison.
Upvotes: 0
Reputation: 928
Another way to solve the problem
function diffArray(arr1, arr2) {
return arr1.concat(arr2).filter(function (val) {
if (!(arr1.includes(val) && arr2.includes(val)))
return val;
});
}
diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]); // return [7, 4, 5]
Also, you can use arrow function syntax:
const diffArray = (arr1, arr2) => arr1.concat(arr2)
.filter(val => !(arr1.includes(val) && arr2.includes(val)));
diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]); // return [7, 4, 5]
Upvotes: 16
Reputation: 1725
Here is a slightly modified version that uses an Object to store the hashes can handle numbers as well as strings in arrays.
function arrDiff(a, b) {
const hash = {};
a.forEach(n => { hash[n] = n; });
b.forEach(n => {
if (hash[n]) {
delete hash[n];
} else {
hash[n] = n;
}
});
return Object.values(hash);
}
Upvotes: 0
Reputation: 1441
Based on previous answers... depends if you want an efficient or "nice oneliner" solution.
There are 3 approaches in general...
"manual iterative" (using indexOf) - naive with O(n2) complexity (slow)
var array_diff_naive = function(a,b){
var i, la = a.length, lb = b.length, res = [];
if (!la) return b; else if (!lb) return a;
for (i = 0; i < la; i++) {
if (b.indexOf(a[i]) === -1) res.push(a[i]);
}
for (i = 0; i < lb; i++) {
if (a.indexOf(b[i]) === -1) res.push(b[i]);
}
return res;
}
"abstract iterative" (using filter and concat library methods) - syntactic sugar for manual iterative (looks nicer, still sucks)
var array_diff_modern = function(a1,a2){
return a1.filter(function(v) { return !a2.includes(v); } )
.concat(a2.filter(function(v) { return !a1.includes(v);}));
}
"using hashtable" (using object keys) - much more efficient - only O(n), but has slightly limited range of input array values
var array_diff_hash = function(a1,a2){
var a = [], diff = [];
for (var i = 0; i < a1.length; i++) {
a[a1[i]] = true;
}
for (var i = 0; i < a2.length; i++) {
if (a[a2[i]]) {
delete a[a2[i]];
} else {
a[a2[i]] = true;
}
}
for (var k in a) {
diff.push(k);
}
return diff;
}
See this on jsperf
https://jsperf.com/array-diff-algo
Upvotes: 1
Reputation: 3196
Use extra memory to do this. That way you can solve it with less time complexity, O(n) instead of o(n*n).
function getDiff(arr1,arr2){
let k = {};
let diff = []
arr1.map(i=>{
if (!k.hasOwnProperty(i)) {
k[i] = 1
}
}
)
arr2.map(j=>{
if (!k.hasOwnProperty(j)) {
k[j] = 1;
} else {
k[j] = 2;
}
}
)
for (var i in k) {
if (k[i] === 1)
diff.push(+i)
}
return diff
}
getDiff([4, 3, 52, 3, 5, 67, 9, 3],[4, 5, 6, 75, 3, 334, 5, 5, 6])
Upvotes: 3
Reputation: 2163
I agree with the solution of @luis-sieira
I created bit self explanatory function for beginners to understand easily step by step:
function difference(oneArr, twoArr){
var newArr = [];
newArr = oneArr.filter((item)=>{
return !twoArr.includes(item)
});
console.log(newArr)
let arr = twoArr.filter((item)=>{
return !oneArr.includes(item)
});
newArr = newArr.concat(arr);
console.log(newArr)
}
difference([1, 2, 3, 5], [1, 2, 3, 4, 5])
Upvotes: 1
Reputation: 2389
A cleaner approach in ES6 is the following solution.
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
a2.filter(d => !a1.includes(d)) // gives ["c", "d"]
a2.filter(d => a1.includes(d)) // gives ["a", "b"]
[ ...a2.filter(d => !a1.includes(d)),
...a1.filter(d => !a2.includes(d)) ]
Upvotes: 78
Reputation: 48693
Based on Thinker's answer, but allows duplicates.
The map increments map values as they appear, and decrements them if they are in the other array.
Any leftover will be included in the difference.
function diff(curr, prev) {
let a = curr.split('').sort(), b = prev.split('').sort(), c = arrDiff(a, b);
console.log(JSON.stringify(a), '-', JSON.stringify(b), '=', JSON.stringify(c));
return c;
}
function arrDiff(larger, smaller) {
var entries = {};
for (var i = 0; i < larger.length; i++) {
entries[larger[i]] = (entries[larger[i]] || 0) + 1;
}
for (var i = 0; i < smaller.length; i++) {
if (entries[smaller[i]]) {
entries[smaller[i]] -= 1;
} else {
entries[smaller[i]] = (entries[smaller[i]] || 0) + 1;
}
}
return Object.keys(entries).sort().reduce((diff, key) => {
if (entries[key] > 0) {
for (var i = 0; i < entries[key]; i++) {
diff.push(key);
}
}
return diff;
}, []);
}
// Smaller is a subset of Larger
console.log('Result:', JSON.stringify(diff('ENLIGHTEN', 'LENGTHEN'))); // [ I ]
console.log('Result:', JSON.stringify(diff('STRENGTH', 'TENTHS'))); // [ G, R ]
// Both have a unique value
console.log('Result:', JSON.stringify(diff('BUBBLE', 'RUBBLE'))); // [ B, R ]
.as-console-wrapper { top: 0; max-height: 100% !important; }
Upvotes: 0
Reputation: 1887
if you don't care about original arrays and have no problem to edit them then this is quicker algorithm:
let iterator = arrayA.values()
let result = []
for (entryA of iterator) {
if (!arrayB.includes(entryA)) {
result.push(entryA)
} else {
arrayB.splice(arrayB.indexOf(entryA), 1)
}
}
result.push(...arrayB)
return result
Upvotes: 0
Reputation: 106
Similar to Ian Grainger's solution (but in typescript):
function findDiffs(arrayOne: string[], arrayTwo: string[]) {
let onlyInArrayOne = []
let onlyInArrayTwo = []
let share = []
let [arrayOneCopy, arrayTwoCopy] = [[...arrayOne], [...arrayTwo]]
arrayOneCopy.sort(); arrayTwoCopy.sort()
while (arrayOneCopy.length !== 0 && arrayTwoCopy.length !== 0) {
if (arrayOneCopy[0] == arrayTwoCopy[0]) {
share.push(arrayOneCopy[0])
arrayOneCopy.splice(0, 1)
arrayTwoCopy.splice(0, 1)
}
if (arrayOneCopy[0] < arrayTwoCopy[0]) {
onlyInArrayOne.push(arrayOneCopy[0])
arrayOneCopy.splice(0, 1)
}
if (arrayOneCopy[0] > arrayTwoCopy[0]) {
onlyInArrayTwo.push(arrayTwoCopy[0])
arrayTwoCopy.splice(0, 1)
}
}
onlyInArrayTwo = onlyInArrayTwo.concat(arrayTwoCopy)
onlyInArrayOne = onlyInArrayOne.concat(arrayOneCopy)
return {
onlyInArrayOne,
onlyInArrayTwo,
share,
diff: onlyInArrayOne.concat(onlyInArrayTwo)
}
}
// arrayOne: [ 'a', 'b', 'c', 'm', 'y' ]
// arrayTwo: [ 'c', 'b', 'f', 'h' ]
//
// Results:
// {
// onlyInArrayOne: [ 'a', 'm', 'y' ],
// onlyInArrayTwo: [ 'f', 'h' ],
// share: [ 'b', 'c' ],
// diff: [ 'a', 'm', 'y', 'f', 'h' ]
// }
Upvotes: 2
Reputation: 7045
yet another answer, but seems nobody mentioned jsperf where they compare several algorithms and technology support: https://jsperf.com/array-difference-javascript seems using filter gets the best results. thanks
Upvotes: 3
Reputation: 33
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
var diff = [];
for (var i in a2) {
var found = false;
for (var j in a1) {
if (a2[i] === a1[j]) found = true;
}
if (found === false) diff.push(a2[i]);
}
That simple. Could use with objects also, checking one property of object. Like,
if (a2[i].id === a1[j].id) found = true;
Upvotes: 2