Есть ли способ вернуть разницу между двумя массивами в JavaScript?
Например:
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
// need ["c", "d"]
Есть ли способ вернуть разницу между двумя массивами в JavaScript?
Например:
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
// need ["c", "d"]
Я предполагаю, что вы сравниваете нормальный массив. Если нет, вам нужно изменить цикл for на цикл for.. in.
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")); Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
////////////////////
// Examples
////////////////////
[1,2,3,4,5,6].diff( [3,4,5] );
// => [1, 2, 6]
["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]);
// => ["test5", "test6"]
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
////////////////////
// Examples
////////////////////
var dif1 = [1,2,3,4,5,6].diff( [3,4,5] );
console.log(dif1); // => [1, 2, 6]
var dif2 = ["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]);
console.log(dif2); // => ["test5", "test6"] Есть лучший способ использования ES7:
пересечение
let intersection = arr1.filter(x => arr2.includes(x));
Для [1,2,3] [2,3] это даст [2,3]. С другой стороны, для [1,2,3] [2,3,5] вернется то же самое.
разница
let difference = arr1.filter(x => !arr2.includes(x));
Для [1,2,3] [2,3] это даст [1]. С другой стороны, для [1,2,3] [2,3,5] вернется то же самое.
Для симметричной разности вы можете сделать:
let difference = arr1
.filter(x => !arr2.includes(x))
.concat(arr2.filter(x => !arr1.includes(x)));
Таким образом, вы получите массив, содержащий все элементы arr1, которых нет в arr2, и наоборот
Как отметил @Joshaven Potter в своем ответе, вы можете добавить это в Array.prototype, чтобы его можно было использовать следующим образом:
Array.prototype.diff = arr1.filter(x => arr2.includes(x));
[1, 2, 3].diff([2, 3])
Это самый простой способ получить именно тот результат, который вы ищете, используя jQuery:
var diff = $(old_array).not(new_array).get();
diff теперь содержит то, что было в old_array, которое не находится в new_array
Разностный метод в Underscore (или его замена, Lo-Dash) может сделать это тоже:
(R)eturns the values from array that are not present in the other arrays
_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]
Как и любая функция Underscore, вы также можете использовать ее в более объектно-ориентированном стиле:
_([1, 2, 3, 4, 5]).difference([5, 2, 10]);
Есть две возможные интуиции для "разницы". Я позволю тебе выбрать, какой ты хочешь. Скажите, что у вас есть:
var a1 = ['a', 'b' ];
var a2 = [ 'b', 'c'];
Если вы хотите получить ['a'], используйте эту функцию:
function difference(a1, a2) {
var result = [];
for (var i = 0; i < a1.length; i++) {
if (a2.indexOf(a1[i]) === -1) {
result.push(a1[i]);
}
}
return result;
}
Если вы хотите получить ['a', 'c'] (все элементы, содержащиеся в a1 или a2, но не оба - так называемая симметричная разность), используйте эту функцию:
function symmetricDifference(a1, a2) {
var result = [];
for (var i = 0; i < a1.length; i++) {
if (a2.indexOf(a1[i]) === -1) {
result.push(a1[i]);
}
}
for (i = 0; i < a2.length; i++) {
if (a1.indexOf(a2[i]) === -1) {
result.push(a2[i]);
}
}
return result;
}
Если вы используете lodash, вы можете использовать _.difference(a1, a2) (пример 1 выше) или _.xor(a1, a2) (случай 2).
Если вы используете Underscore.js, вы можете использовать функцию _.difference(a1, a2) для случая 1.
Код выше работает во всех браузерах. Тем не менее, для больших массивов более чем около 10000 элементов он становится довольно медленным, поскольку он имеет сложность O (n²). Во многих современных браузерах мы можем использовать объект ES6 Set, чтобы ускорить работу. Lodash автоматически использует Set, когда он доступен. Если вы не используете lodash, используйте следующую реализацию, вдохновленную Сообщение в блоге Axel Rauschmayer:
function difference(a1, a2) {
var a2Set = new Set(a2);
return a1.filter(function(x) { return !a2Set.has(x); });
}
function symmetricDifference(a1, a2) {
return difference(a1, a2).concat(difference(a2, a1));
}
Поведение для всех примеров может быть неожиданным или неочевидным, если вы заботитесь о - 0, +0, NaN или разреженных массивах. (Для большинства применений это не имеет значения.)
Чтобы получить симметричную разницу, необходимо сравнить массивы обоими способами (или во всех случаях в случае нескольких массивов).
// diff between just two arrays:
function arrayDiff(a, b) {
return [
...a.filter(x => !b.includes(x)),
...b.filter(x => !a.includes(x))
];
}
// diff between multiple arrays:
function arrayDiff(...arrays) {
return [].concat(...arrays.map( (arr, i) => {
const others = arrays.slice(0);
others.splice(i, 1);
const unique = [...new Set([].concat(...others))];
return arr.filter(x => !unique.includes(x));
}));
}
// diff between just two arrays:
function arrayDiff(a, b) {
return [
...a.filter(x => b.indexOf(x) === -1),
...b.filter(x => a.indexOf(x) === -1)
];
}
// diff between multiple arrays:
function arrayDiff(...arrays) {
return [].concat(...arrays.map( (arr, i) => {
const others = arrays.slice(0);
others.splice(i, 1);
const unique = [...new Set([].concat(...others))];
return arr.filter(x => unique.indexOf(x) === -1);
}));
}
// diff between just two arrays:
function arrayDiff(a, b) {
var arrays = Array.prototype.slice.call(arguments);
var diff = [];
arrays.forEach(function(arr, i) {
var other = i === 1 ? a : b;
arr.forEach(function(x) {
if (other.indexOf(x) === -1) {
diff.push(x);
}
});
})
return diff;
}
// diff between multiple arrays:
function arrayDiff() {
var arrays = Array.prototype.slice.call(arguments);
var diff = [];
arrays.forEach(function(arr, i) {
var others = arrays.slice(0);
others.splice(i, 1);
var otherValues = Array.prototype.concat.apply([], others);
var unique = otherValues.filter(function (x, j) {
return otherValues.indexOf(x) === j;
});
diff = diff.concat(arr.filter(x => unique.indexOf(x) === -1));
});
return diff;
}
Пример:
// diff between two arrays:
const a = ['a', 'd', 'e'];
const b = ['a', 'b', 'c', 'd'];
arrayDiff(a, b); // (3) ["e", "b", "c"]
// diff between multiple arrays
const a = ['b', 'c', 'd', 'e', 'g'];
const b = ['a', 'b'];
const c = ['a', 'e', 'f'];
arrayDiff(a, b, c); // (4) ["c", "d", "g", "f"]
function arrayDiffByKey(key, ...arrays) {
return [].concat(...arrays.map( (arr, i) => {
const others = arrays.slice(0);
others.splice(i, 1);
const unique = [...new Set([].concat(...others))];
return arr.filter( x =>
!unique.some(y => x[key] === y[key])
);
}));
}
Пример:
const a = [{k:1}, {k:2}, {k:3}];
const b = [{k:1}, {k:4}, {k:5}, {k:6}];
const c = [{k:3}, {k:5}, {k:7}];
arrayDiffByKey('k', a, b, c); // (4) [{k:2}, {k:4}, {k:6}, {k:7}]
В этом случае вы можете использовать Set. Он оптимизирован для такого рода операций (объединение, пересечение, разность).
Удостоверьтесь, что это применимо к вашему случаю, если оно не позволяет дублировать.
var a = new JS.Set([1,2,3,4,5,6,7,8,9]);
var b = new JS.Set([2,4,6,8]);
a.difference(b)
// -> Set{1,3,5,7,9}
function diff(a1, a2) {
return a1.concat(a2).filter(function(val, index, arr){
return arr.indexOf(val) === arr.lastIndexOf(val);
});
}
Объедините оба массива, уникальные значения появятся только один раз, поэтому indexOf() будет таким же, как lastIndexOf().
чтобы вычесть один массив из другого, просто используйте фрагмент ниже:
var a1 = ['1','2','3','4','6'];
var a2 = ['3','4','5'];
var items = new Array();
items = jQuery.grep(a1,function (item) {
return jQuery.inArray(item, a2) < 0;
});
Он вернет ['1,' 2 ',' 6 '], которые являются элементами первого массива, которые не существуют во втором.
Поэтому, согласно вашему образцу проблемы, следующий код является точным решением:
var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];
var _array = new Array();
_array = jQuery.grep(array2, function (item) {
return jQuery.inArray(item, array1) < 0;
});
Решение с использованием indexOf() будет хорошо для небольших массивов, но по мере их увеличения производительность алгоритма приближается к O(n^2). Здесь решение, которое будет работать лучше для очень больших массивов, используя объекты в качестве ассоциативных массивов для хранения записей массива в виде ключей; он также автоматически удаляет повторяющиеся записи, но работает только со строковыми значениями (или значениями, которые можно безопасно хранить в виде строк):
function arrayDiff(a1, a2) {
var o1={}, o2={}, diff=[], i, len, k;
for (i=0, len=a1.length; i<len; i++) { o1[a1[i]] = true; }
for (i=0, len=a2.length; i<len; i++) { o2[a2[i]] = true; }
for (k in o1) { if (!(k in o2)) { diff.push(k); } }
for (k in o2) { if (!(k in o1)) { diff.push(k); } }
return diff;
}
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
arrayDiff(a1, a2); // => ['c', 'd']
arrayDiff(a2, a1); // => ['c', 'd']
С появлением ES6 с наборами и оператором splat (во время работы только в Firefox, проверьте таблицу совместимости), вы можете напишите следующий вкладыш:
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)))];
что приведет к [ "c", "d" ].
Вычисление difference между двумя массивами является одной из операций Set. Термин уже указывает, что должен использоваться родной тип Set, чтобы увеличить скорость поиска. Во всяком случае, есть три перестановки, когда вы вычисляете разницу между двумя наборами:
[+left difference] [-intersection] [-right difference]
[-left difference] [-intersection] [+right difference]
[+left difference] [-intersection] [+right difference]
Вот функциональное решение, которое отражает эти перестановки.
difference:
// small, reusable auxiliary functions
const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
// left difference
const differencel = xs => ys => {
const zs = createSet(ys);
return filter(x => zs.has(x)
? false
: true
) (xs);
};
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];
// run the computation
console.log( differencel(xs) (ys) ); Вышеприведенный ответ Джошавеном Поттером велик. Но он возвращает элементы в массиве B, которые не находятся в массиве C, но не наоборот. Например, если var a=[1,2,3,4,5,6].diff( [3,4,5,7]);, то он будет выводить: == > [1,2,6], но не [1,2,6,7], что является фактической разницей между ними. Вы все равно можете использовать код Поттера выше, но просто повторите сравнение один раз назад:
Array.prototype.diff = function(a) {
return this.filter(function(i) {return !(a.indexOf(i) > -1);});
};
////////////////////
// Examples
////////////////////
var a=[1,2,3,4,5,6].diff( [3,4,5,7]);
var b=[3,4,5,7].diff([1,2,3,4,5,6]);
var c=a.concat(b);
console.log(c);
Это должно выводить: [ 1, 2, 6, 7 ]
Другой способ решения проблемы
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]
Array.prototype.difference = function(e) {
return this.filter(function(i) {return e.indexOf(i) < 0;});
};
eg:-
[1,2,3,4,5,6,7].difference( [3,4,5] );
=> [1, 2, 6 , 7]
Как насчет этого:
Array.prototype.contains = function(needle){
for (var i=0; i<this.length; i++)
if (this[i] == needle) return true;
return false;
}
Array.prototype.diff = function(compare) {
return this.filter(function(elem) {return !compare.contains(elem);})
}
var a = new Array(1,4,7, 9);
var b = new Array(4, 8, 7);
alert(a.diff(b));
Итак, вы можете сделать array1.diff(array2), чтобы получить их различие (Ужасная временная сложность для алгоритма - O (array1.length x array2.length), я считаю)
Очень простое решение с функцией фильтрации JavaScript:
var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
function diffArray(arr1, arr2) {
var newArr = [];
var myArr = arr1.concat(arr2);
newArr = myArr.filter(function(item){
return arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0;
});
alert(newArr);
}
diffArray(a1, a2); Используя http://phrogz.net/JS/ArraySetMath.js, вы можете:
var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];
var array3 = array2.subtract( array1 );
// ["test5", "test6"]
var array4 = array1.exclusion( array2 );
// ["test5", "test6"]
function diffArray(arr1, arr2) {
var newArr = arr1.concat(arr2);
return newArr.filter(function(i){
return newArr.indexOf(i) == newArr.lastIndexOf(i);
});
}
это работает для меня
filter)fn параметр обратного вызова, который позволяет указать, как сравнивать элементы массива
function diff(a, b, fn){
var max = Math.max(a.length, b.length);
d = [];
fn = typeof fn === 'function' ? fn : false
for(var i=0; i < max; i++){
var ac = i < a.length ? a[i] : undefined
bc = i < b.length ? b[i] : undefined;
for(var k=0; k < max; k++){
ac = ac === undefined || (k < b.length && (fn ? fn(ac, b[k]) : ac == b[k])) ? undefined : ac;
bc = bc === undefined || (k < a.length && (fn ? fn(bc, a[k]) : bc == a[k])) ? undefined : bc;
if(ac == undefined && bc == undefined) break;
}
ac !== undefined && d.push(ac);
bc !== undefined && d.push(bc);
}
return d;
}
alert(
"Test 1: " +
diff(
[1, 2, 3, 4],
[1, 4, 5, 6, 7]
).join(', ') +
"\nTest 2: " +
diff(
[{id:'a',toString:function(){return this.id}},{id:'b',toString:function(){return this.id}},{id:'c',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
[{id:'a',toString:function(){return this.id}},{id:'e',toString:function(){return this.id}},{id:'f',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
function(a, b){ return a.id == b.id; }
).join(', ')
); Это работает: в основном объедините два массива, найдите дубликаты и нажмите то, что не дублируется в новый массив, который является разницей.
function diff(arr1, arr2) {
var newArr = [];
var arr = arr1.concat(arr2);
for (var i in arr){
var f = arr[i];
var t = 0;
for (j=0; j<arr.length; j++){
if(arr[j] === f){
t++;
}
}
if (t === 1){
newArr.push(f);
}
}
return newArr;
} Просто думая... ради задачи;-) будет ли это работать... (для базовых массивов строк, чисел и т.д.) нет вложенных массивов
function diffArrays(arr1, arr2, returnUnion){
var ret = [];
var test = {};
var bigArray, smallArray, key;
if(arr1.length >= arr2.length){
bigArray = arr1;
smallArray = arr2;
} else {
bigArray = arr2;
smallArray = arr1;
}
for(var i=0;i<bigArray.length;i++){
key = bigArray[i];
test[key] = true;
}
if(!returnUnion){
//diffing
for(var i=0;i<smallArray.length;i++){
key = smallArray[i];
if(!test[key]){
test[key] = null;
}
}
} else {
//union
for(var i=0;i<smallArray.length;i++){
key = smallArray[i];
if(!test[key]){
test[key] = true;
}
}
}
for(var i in test){
ret.push(i);
}
return ret;
}
array1 = "test1", "test2","test3", "test4", "test7"
array2 = "test1", "test2","test3","test4", "test5", "test6"
diffArray = diffArrays(array1, array2);
//returns ["test5","test6","test7"]
diffArray = diffArrays(array1, array2, true);
//returns ["test1", "test2","test3","test4", "test5", "test6","test7"]
Обратите внимание, что сортировка, скорее всего, не будет такой, как указано выше... но при желании вызовите .sort() в массиве, чтобы отсортировать ее.
Мне нужна была аналогичная функция, которая использовалась в старом массиве и новом массиве и дала мне массив добавленных элементов и массив удаленных элементов, и я хотел, чтобы он был эффективным (так что нет .contains!).
Вы можете играть с моим предлагаемым решением здесь: http://jsbin.com/osewu3/12.
Может ли кто-нибудь увидеть какие-либо проблемы/улучшения в этом алгоритме? Спасибо!
Список кодов:
function diff(o, n) {
// deal with empty lists
if (o == undefined) o = [];
if (n == undefined) n = [];
// sort both arrays (or this won't work)
o.sort(); n.sort();
// don't compare if either list is empty
if (o.length == 0 || n.length == 0) return {added: n, removed: o};
// declare temporary variables
var op = 0; var np = 0;
var a = []; var r = [];
// compare arrays and add to add or remove lists
while (op < o.length && np < n.length) {
if (o[op] < n[np]) {
// push to diff?
r.push(o[op]);
op++;
}
else if (o[op] > n[np]) {
// push to diff?
a.push(n[np]);
np++;
}
else {
op++;np++;
}
}
// add remaining items
if( np < n.length )
a = a.concat(n.slice(np, n.length));
if( op < o.length )
r = r.concat(o.slice(op, o.length));
return {added: a, removed: r};
}
Маленькое исправление для лучшего ответа
function arr_diff(a1, a2)
{
var a=[], diff=[];
for(var i=0;i<a1.length;i++)
a[a1[i]]=a1[i];
for(var i=0;i<a2.length;i++)
if(a[a2[i]]) delete a[a2[i]];
else a[a2[i]]=a2[i];
for(var k in a)
diff.push(a[k]);
return diff;
}
это будет учитывать текущий тип элемента. b/c, когда мы делаем [a1 [i]], он преобразует значение в строку из своего исходного значения, поэтому мы потеряли фактическое значение.
Вы можете использовать underscore.js: http://underscorejs.org/#intersection
Вам нужны методы для массива:
_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]
_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]
Это было вдохновлено принятым ответом Мыслителя, но ответ Мыслителя, похоже, предполагает, что массивы являются множествами. Он разваливается, если массивы [ "1", "2" ] и [ "1", "1", "2", "2" ]
Разница между этими массивами составляет [ "1", "2" ]. Следующее решение - O (n * n), поэтому не идеально, но если у вас большие массивы, у него есть преимущества памяти по сравнению с решением Thinker.
Если вы имеете дело с наборами в первую очередь, решение Thinker определенно лучше. Если у вас есть более новая версия Javascript с доступом к фильтрам, вы также должны использовать их. Это только для тех, кто не имеет дело с наборами и использует старую версию JavaScript (по какой-либо причине)...
if (!Array.prototype.diff) {
Array.prototype.diff = function (array) {
// if the other array is a falsy value, return a copy of this array
if ((!array) || (!Array.prototype.isPrototypeOf(array))) {
return this.slice(0);
}
var diff = [];
var original = this.slice(0);
for(var i=0; i < array.length; ++i) {
var index = original.indexOf(array[i]);
if (index > -1) {
original.splice(index, 1);
} else {
diff.push(array[i]);
}
}
for (var i=0; i < original.length; ++i) {
diff.push(original[i]);
}
return diff;
}
}
function diff(arr1, arr2) {
var filteredArr1 = arr1.filter(function(ele) {
return arr2.indexOf(ele) == -1;
});
var filteredArr2 = arr2.filter(function(ele) {
return arr1.indexOf(ele) == -1;
});
return filteredArr1.concat(filteredArr2);
}
diff([1, "calf", 3, "piglet"], [1, "calf", 3, 4]); // Log ["piglet",4]
//подход es6
function diff(a, b) {
var u = a.slice(); //dup the array
b.map(e => {
if (u.indexOf(e) > -1) delete u[u.indexOf(e)]
else u.push(e) //add non existing item to temp array
})
return u.filter((x) => {return (x != null)}) //flatten result
}
Если массивы не имеют простых типов, то один из приведенных ответов может быть адаптирован:
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.map(function(e) { return JSON.stringify(e); }).indexOf(JSON.stringify(i)) < 0;});
};
Этот метод работает с массивами сложных объектов.