Як отримати різницю між двома масивами в JavaScript?


751

Чи є спосіб повернути різницю між двома масивами в JavaScript?

Наприклад:

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

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

9
Симетричний чи несиметричний?
Гонки легкості по орбіті

Завдяки новій функції ES6 це можна зробити як простий вкладиш (це займе багато часу, щоб мати можливість користуватися у всіх основних браузерах). У будь-якому випадку перевірити мою відповідь
Сальвадор Далі

1
важливим аспектом рішення є продуктивність. асимптотична часова складність цього типу операцій - іншими мовами - це O(a1.length x log(a2.length))- чи можлива ця ефективність у JavaScript?
Рауль

Відповіді:


218

Я припускаю, що ви порівнюєте звичайний масив. Якщо ні, вам потрібно змінити цикл for для циклу на " .." .

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

Краще рішення, якщо вас не хвилює зворотна сумісність, - використовувати фільтр. Але все-таки це рішення працює.


46
Це може спрацювати, але це три цикли, щоб виконати те, що можна зробити в одному рядку коду, використовуючи метод фільтра Array.
Джошавен Поттер

9
Просто щоб бути ясно, це реалізує симетрична різниця по а1 і а2 , в відміну від інших відповідей опубліковані тут.
200_успіх

25
Це не найкраща відповідь, але я даю це благодійне становище, щоб допомогти компенсувати несправедливі голоси. Лише неправильні відповіді слід оскаржувати, і якби я працював над проектом із чіткими браузерами за обсягом (важкі часи трапляються), ця відповідь може бути навіть корисною.
Майкл Шепер

3
Можу чи я дізнатися , що станеться , коли var a1 = ['a', 'b'];і var a2 = ['a', 'b', 'c', 'd', 'b'];, він буде повертати невірну відповідь , то є ['c', 'd', 'b']замість ['c', 'd'].
skbly7

4
Найшвидший спосіб - найбільш очевидно наївне рішення. Я перевірив усі запропоновані рішення на симетричну різницю в цій нитці, і переможець:function diff2(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; }
Nomaed

1224

Є кращий спосіб використання 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 = function(arr2) { return this.filter(x => !arr2.includes(x)); }
[1, 2, 3].diff([2, 3])

3
Я вважаю за краще перевірити < 0замість== -1
Вік,

1
Обчислення Arrayрізниці - це так зване set operation, тому що пошук властивостей - це саме власне завдання Sets, які на порядок швидше indexOf/ includes. Простіше кажучи, ваше рішення дуже неефективне і досить повільне.

@ftor але з Set, значення повинні бути унікальними, ні?
CervEd

1
@LuisSieira Я розумію, що це буде працювати, [1,2,3] [2,3,5]враховуючи, що цифри є унікальними, але якби ви сказали [1,1,2,3] [1,2,3,5]і очікували, [1]ви не можете використовувати Set. Ваше рішення також не працюватиме: - / Я закінчив створити цю функцію, тому що не міг знайти задовільний спосіб зробити це більш коротко. Якщо у вас є ідеї, як це зробити, я хотів би знати!
CervEd

3
Чи не Array.includes()функція ES7 замість ES6? (1) (2) - і продовжувати, з ES6 ви можете використовувати, Array.some()наприклад let intersection = aArray.filter(a => bArray.some(b => a === b)), ні?
Ярі Кейненен

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

Примітка indexOf та фільтр недоступні, тобто перед ie9.


50
Єдиний важливий браузер, який не підтримує filter і indexOf, - це IE8. IE9 підтримує їх обох. Тож це не помиляється.
Брайан Ларсен

14
ie7 і ie8 як і раніше (на жаль) дуже актуальні, однак ви можете знайти код поліфаузи для обох функцій на сайті MDN: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… developer.mozilla.org/uk/JavaScript/ Довідка / Global_Objects / ... Завантажте код, перелічений у розділі "сумісність", за допомогою IE conditional & BOOM. Ie7 / 8 підтримуються.
1nfiniti

44
Це рішення має час виконання O (n ^ 2), а лінійне рішення було б набагато ефективнішим.
jholloman

75
Якщо ви користуєтесь такою функцією: [1,2,3].diff([3,4,5])вона повернеться [1,2]замість того, [1,2,4,5]щоб вона не вирішила проблему в оригінальному запитанні, про що слід пам’ятати.
Багстер

12
@AlinPurcaru Не підтримується архаїчними браузерами! = Неправильно. Зважаючи на Netscape 2.0, більшість кодів JS тут "неправильне" цим визначенням. Дурно сказати.
NullUserException

304

Це, безумовно, найпростіший спосіб отримати саме той результат, який ви шукаєте, використовуючи jQuery:

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

diffтепер містить те, що було в old_arrayтому, що не вnew_array


4
@Batman Так, але лише якщо вони посилаються на один і той же об’єкт ( {a: 1} != {a: 1}) ( доказ )
Matmarbon

Чи можливо використовувати його з масивами, що містять дані спеціального об'єкта? Я спробував це фактично так само, але не вийшло. Будь-які ідеї будуть дуже вдячні.
LetMeCodeYou

8
Це трюк? Док розглядає цей метод як частина методів DOM Element , а не в якості загального помічника масиву. Тож воно може працювати таким чином зараз, але, можливо, не в майбутніх версіях, оскільки його не планували використовувати таким чином. Хоча я був би радий, якби офіційно це був помічник загального масиву.
robsch

1
@robsch Коли ви використовуєте .notмасив, jQuery використовує вбудовану утиліту, .grep()яка спеціально призначена для фільтрації масивів. Я не бачу, як це змінюється.
суперфонічний

1
@vsync Здається, що ти схожий на симетричну різницю
суперфонічний

158

Метод різниці в підкресленні (або його заміні 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]

Як і будь-яка функція підкреслення, ви також можете використовувати її в більш об'єктно-орієнтованому стилі:

_([1, 2, 3, 4, 5]).difference([5, 2, 10]);

4
Я думаю, що це добре рішення щодо ефективності рішення, особливо якщо подача та підкреслення продовжують боротися за найкращу реалізацію. Крім того, він сумісний з IE6.
mahemoff

4
Остерігайтеся, ця реалізація не працюватиме для масивів об’єктів. Для отримання додаткової інформації див. Stackoverflow.com/q/8672383/14731 .
Гілі

1
Як згадується одна з відповідей, вона працює, якщо це один і той же об’єкт, але не якщо два об'єкти мають однакові властивості. Я думаю, що це нормально, оскільки поняття рівності різняться (наприклад, це може бути також атрибут "id" у деяких додатках). Однак було б добре, якби ви могли пройти тест порівняння для перетину ().
mahemoff

Для нащадків: Лодаш тепер має _.differenceBy (), який приймає зворотний дзвінок для порівняння; якщо ви порівнюєте об'єкти, ви можете скористатися функцією, яка порівнює їх, як вам потрібно.
SomeCallMeTim

2
Будьте уважні, якщо порядок аргументів буде змінено, це не спрацює. напр. _.різниця ([5, 2, 10], [1, 2, 3, 4, 5]); не можете отримати
різницю

79

Простий JavaScript

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

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. Якщо ви хочете отримати ['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;
    }
  2. Якщо ви хочете отримати ['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.

Набір ES6 для дуже великих масивів

Код, описаний вище, працює у всіх браузерах. Однак для великих масивів, що містять понад 10 000 елементів, він стає досить повільним, оскільки має складність O (n²). У багатьох сучасних браузерах ми можемо скористатися Setоб’єктом ES6 для прискорення роботи. 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 або рідкісні масиви. (Для більшості застосувань це не має значення.)


Спасибі. ти врятував мій день. Мені довелося порівняти масиви 300K, і ваше рішення "Set" спрацювало чудово. Це має бути прийнятою відповіддю.
justadev

52

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

введіть тут опис зображення


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

50

Більш чіткий підхід в ES6 - це наступне рішення.

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

Це працює лише в одному напрямку. А тепер уявіть, що a1 = ['a', 'b', 'e']: e не буде видобуто.
імрок

так, так працює різниця в теорії множин. (a2 -a1), що ви шукаєте, це (a2-a1) + (a1-a2)
ifelse.codes

1
@imrok Я вважаю, що це те, що ви шукаєте [... a2.filter (d =>! a1.includes (d)), ... (a1.filter (d =>! a2.includes (d)) )]
ifelse.codes

2
Прекрасне рішення, дякую!
Тіаго Алвеш

40

Ви можете використовувати набір у цьому випадку. Він оптимізований для такого роду операцій (з'єднання, перетину, різниці).

Переконайтеся, що він стосується вашої справи, як тільки він не допускає копій.

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}

4
Це виглядає як приємна бібліотека! Яка ганьба, що ви не можете завантажити лише цю Setфункцію без необхідності отримати все інше ...
Blixt

@Blixt Я вважаю, що ви можете завантажити все це, а також просто включити файл set.js
Самуель Каррійо

Набір реалізований і в закритті Google. closure-library.googlecode.com/svn/docs/…
Бен Флінн


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

Об'єднайте обидва масиви, унікальні значення з’являться лише один раз, тому indexOf () буде таким самим, як lastIndexOf ().


3
Я погоджуюся, що це найчистіший і найпростіший спосіб і приємно, що для нього не потрібно торкатися прототипу. "Якщо ти не можеш пояснити це шестирічному віку, ти сам цього не розумієш". - Альберт Ейнштейн
лакостенікодер

18

щоб відняти один масив від іншого, просто використовуйте фрагмент нижче:

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

14

З приходом 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" ].


Цікаво, як це не так, як це робитиb.filter(x => !a.indexOf(x)))
човен

1
@chovy - це різна складність у часі. Моє рішення - це O(n + m)ваше рішення O(n * m)там, де n і m - довжини масивів. Візьміть довгі списки, і моє рішення запрацює за лічені секунди, а ваше займе години.
Сальвадор Далі

Що щодо порівняння атрибуту списку об’єктів? Чи можливо це за допомогою цього рішення?
chovy

2
a.filter(x => !b1.has(x))простіше. І зверніть увагу на специфікації вимагає тільки складності бути n * f(m) + mз f(m)сублінейним в середньому. Це краще n * m, але не обов’язково n + m.
Оріол

2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];Чому ви створюєте дублікат масиву "a"? Чому ви перетворюєте результат фільтра в набір, а потім знову в масив? Хіба це не рівнозначноvar difference = a.filter(x => !b1.has(x));
Deepak Mittal

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

Право difference:

differencerтривіально. Це просто differencelз перевернутими аргументами. Ви можете написати функцію для зручності:const differencer = flip(differencel) . Це все!

Симетричний difference:

Тепер, коли ми маємо лівий і правий, реалізація симетричного також differenceстає тривіальною:

// small, reusable auxiliary functions

const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const concat = y => xs => xs.concat(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);
};


// symmetric difference

const difference = ys => xs =>
 concat(differencel(xs) (ys)) (flip(differencel) (xs) (ys));

// 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( difference(xs) (ys) );

Я думаю, цей приклад є гарною відправною точкою для отримання враження, що означає функціональне програмування:

Програмування за допомогою будівельних блоків, які можна з'єднати між собою різними способами.


12

Для 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']

Ви хочете використовувати Object.hasOwnProperty (), коли ви робите об'єкт "для". Інакше ви ризикуєте переглядати кожне поле, додане до прототипу об'єкта за замовчуванням. (Або просто батьківського об'єкта) Також вам потрібні лише дві петлі, одна для створення хеш-таблиці, а інша шукає цю хеш-таблицю.
jholloman

1
@jholloman Я з повагою не згоден . Тепер, коли ми можемо контролювати перерахування будь-якого майна, імовірно, ви повинні включати будь-яке майно, яке ви отримаєте під час перерахування.
Фрогз

1
@Phrogz Хороший момент, якщо ви переживаєте лише про сучасні браузери. На жаль, мені доводиться підтримувати IE7 на роботі, тому кам’яний вік - це мій подумок думок за замовчуванням, і ми не схильні використовувати лайни.
jholloman

10

Наведена вище відповідь Джошавена Поттера чудова. Але він повертає елементи масиву 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 ]


9

Ще один спосіб вирішення проблеми

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]

Також ви можете використовувати синтаксис функції стрілки:

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]

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

Ніколи не слід розширювати рідний об’єкт таким чином. Якщо стандарт вводить differenceяк функцію в майбутній версії, а ця функція має інший підпис функції, ніж ваша, вона порушить ваш код або іноземні бібліотеки, які використовують цю функцію.
t.niese

7

Дуже просте рішення з функцією фільтра 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);


6

Як щодо цього:

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) Я вірю)


Використання параметра фільтра - прекрасна ідея ... однак, вам не потрібно створювати метод містить для масиву. Я перетворив вашу ідею в один вкладиш ... Дякую за натхнення!
Джошавен Поттер

Вам не потрібно визначати функцію містить (). JS include () робить те ж саме.
Da Man

4

За допомогою 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"]

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

це для мене працює


3
  • Чисте рішення JavaScript (немає бібліотек)
  • Сумісний із старими браузерами (не використовується filter)
  • O (n ^ 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(', ')
);


Ви можете кешувати значення довжини, щоб видавити трохи більше швидкості. Я хотів порекомендувати доступ до елементів масиву, не перевіряючи їх довжину, але, мабуть, проста перевірка дає майже 100-кратне прискорення.
Слотос

Немає підстав для кешування lengthзначень. Це вже звичайна власність. jsperf.com/array-length-caching
vp_arth

3

Це працює: в основному об'єднайте два масиви, шукайте дублікати і висувайте те, що не дублюється, у новий масив, що є різницею.

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


3

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

3

Симетрична та лінійна складність . Потрібен ES6.

function arrDiff(arr1, arr2) {
    var arrays = [arr1, arr2].sort((a, b) => a.length - b.length);
    var smallSet = new Set(arrays[0]);

    return arrays[1].filter(x => !smallSet.has(x));
}

3

ще одна відповідь, але, схоже, ніхто не згадав jsperf, де вони порівнюють декілька алгоритмів та технологічну підтримку: https://jsperf.com/array-difference-javascript, схоже, використання фільтра дає найкращі результати. Дякую


2

Думаю просто ... заради виклику ;-) чи не спрацює це ... (для основних масивів рядків, чисел тощо) не вкладених масивів

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 () на масив, щоб сортувати його.


2

Я хотів подібну функцію, яка взяла в старий масив і новий масив і дала мені масив доданих елементів і масив видалених елементів, і я хотів, щоб вона була ефективною (так, ні. Не містить!).

Ви можете пограти з моїм запропонованим рішенням тут: 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}; 
}

2

Я шукав просту відповідь, яка не передбачала використання різних бібліотек, і я придумав свою власну, яку, на мою думку, тут не згадували. Я не знаю, наскільки це ефективно чи що-небудь, але це працює;

    function find_diff(arr1, arr2) {
      diff = [];
      joined = arr1.concat(arr2);
      for( i = 0; i <= joined.length; i++ ) {
        current = joined[i];
        if( joined.indexOf(current) == joined.lastIndexOf(current) ) {
          diff.push(current);
        }
      }
      return diff;
    }

Для мого коду мені потрібні також копії дублікатів, але я думаю, що це не завжди є кращим.

Я думаю, головний недолік - це потенційне порівняння багатьох варіантів, які вже були відхилені.


2

виправлення бітбіта для найкращої відповіді

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]], воно перетворює значення в рядок з його орогінального значення, тому ми втратили фактичне значення.


Це все ще не вдається для масиву об'єктів. var a = [{a: 1}], b = [{b: 2}] arr_diff (a, b) == [].
EricP
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.