Как получить разницу между двумя массивами в JavaScript?

Есть ли способ вернуть разницу между двумя массивами в JavaScript?

Например:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

// need ["c", "d"]

Ответ 1

Я предполагаю, что вы сравниваете нормальный массив. Если нет, вам нужно изменить цикл 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"));

Ответ 2

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"]

Ответ 3

Есть лучший способ использования ES7:

пересечение

 let intersection = arr1.filter(x => arr2.includes(x));

Intersection difference Venn Diagram

Для [1,2,3] [2,3] это даст [2,3]. С другой стороны, для [1,2,3] [2,3,5] вернется то же самое.

разница

let difference = arr1.filter(x => !arr2.includes(x));

Right difference Venn Diagram

Для [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)));

Symmetric difference Venn Diagram

Таким образом, вы получите массив, содержащий все элементы arr1, которых нет в arr2, и наоборот

Как отметил @Joshaven Potter в своем ответе, вы можете добавить это в Array.prototype, чтобы его можно было использовать следующим образом:

Array.prototype.diff = arr1.filter(x => arr2.includes(x));
[1, 2, 3].diff([2, 3])

Ответ 4

Это самый простой способ получить именно тот результат, который вы ищете, используя jQuery:

var diff = $(old_array).not(new_array).get();

diff теперь содержит то, что было в old_array, которое не находится в new_array

Ответ 5

Разностный метод в 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]);

Ответ 6

Обычный JavaScript

Есть две возможные интуиции для "разницы". Я позволю тебе выбрать, какой ты хочешь. Скажите, что у вас есть:

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/Underscore

Если вы используете lodash, вы можете использовать _.difference(a1, a2) (пример 1 выше) или _.xor(a1, a2) (случай 2).

Если вы используете Underscore.js, вы можете использовать функцию _.difference(a1, a2) для случая 1.

ES6 Set, для очень больших массивов

Код выше работает во всех браузерах. Тем не менее, для больших массивов более чем около 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 или разреженных массивах. (Для большинства применений это не имеет значения.)

Ответ 7

Чтобы получить симметричную разницу, необходимо сравнить массивы обоими способами (или во всех случаях в случае нескольких массивов).

enter image description here


ES7 (ECMAScript 2016)

// 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));
    }));
}

ES6 (ECMAScript 2015)

// 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);
    }));
}

ES5 (ECMAScript 5.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}]

Ответ 8

В этом случае вы можете использовать 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}

Ответ 9

function diff(a1, a2) {
  return a1.concat(a2).filter(function(val, index, arr){
    return arr.indexOf(val) === arr.lastIndexOf(val);
  });
}

Объедините оба массива, уникальные значения появятся только один раз, поэтому indexOf() будет таким же, как lastIndexOf().

Ответ 10

чтобы вычесть один массив из другого, просто используйте фрагмент ниже:

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;
});

Ответ 11

Решение с использованием 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']

Ответ 12

С появлением 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" ].

Ответ 13

Функциональный подход с ES2015

Вычисление 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) );

Ответ 14

Вышеприведенный ответ Джошавеном Поттером велик. Но он возвращает элементы в массиве 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 ]

Ответ 15

Другой способ решения проблемы

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]

Ответ 16

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]

Ответ 17

Как насчет этого:

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), я считаю)

Ответ 18

Очень простое решение с функцией фильтрации 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);

Ответ 19

Используя 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"]

Ответ 20

function diffArray(arr1, arr2) {
  var newArr = arr1.concat(arr2);
  return newArr.filter(function(i){
    return newArr.indexOf(i) == newArr.lastIndexOf(i);
  });
}

это работает для меня

Ответ 21

  • Чистое решение JavaScript (без библиотек)
  • Совместимость со старыми браузерами (не использует filter)
  • О (п ^ 2)
  • Необязательный 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(', ')
);

Ответ 22

Это работает: в основном объедините два массива, найдите дубликаты и нажмите то, что не дублируется в новый массив, который является разницей.

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;
}

Ответ 23

Просто думая... ради задачи;-) будет ли это работать... (для базовых массивов строк, чисел и т.д.) нет вложенных массивов

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() в массиве, чтобы отсортировать ее.

Ответ 24

Мне нужна была аналогичная функция, которая использовалась в старом массиве и новом массиве и дала мне массив добавленных элементов и массив удаленных элементов, и я хотел, чтобы он был эффективным (так что нет .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}; 
}

Ответ 25

Маленькое исправление для лучшего ответа

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]], он преобразует значение в строку из своего исходного значения, поэтому мы потеряли фактическое значение.

Ответ 26

Вы можете использовать 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]

Ответ 27

Это было вдохновлено принятым ответом Мыслителя, но ответ Мыслителя, похоже, предполагает, что массивы являются множествами. Он разваливается, если массивы [ "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;
    }
}   

Ответ 28

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]

Ответ 29

//подход 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
}

Ответ 30

Если массивы не имеют простых типов, то один из приведенных ответов может быть адаптирован:

Array.prototype.diff = function(a) {
        return this.filter(function(i) {return a.map(function(e) { return JSON.stringify(e); }).indexOf(JSON.stringify(i)) < 0;});
    };

Этот метод работает с массивами сложных объектов.