Есть ли способ вернуть разницу между двумя массивами в 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;});
};
Этот метод работает с массивами сложных объектов.