Bagaimana cara mendapatkan perbedaan antara dua array dalam JavaScript?


754

Apakah ada cara untuk mengembalikan perbedaan antara dua array dalam JavaScript?

Sebagai contoh:

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

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

9
Simetris atau non-simetris?
Lightness Races dalam Orbit

Dengan fungsi ES6 baru ini dapat dilakukan sebagai satu liner sederhana (akan membutuhkan banyak waktu untuk dapat digunakan di semua browser utama). Dalam hal apa pun periksa jawaban
Salvador Dali

1
aspek penting dari solusi adalah kinerja. kompleksitas waktu asimptotik dari jenis operasi ini - dalam bahasa lain - apakah O(a1.length x log(a2.length))- apakah kinerja ini dimungkinkan dalam JavaScript?
Raul

Jawaban:


219

Saya berasumsi Anda membandingkan array normal. Jika tidak, Anda perlu mengubah untuk loop untuk sebuah untuk .. di lingkaran.

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

Solusi yang lebih baik, jika Anda tidak peduli tentang kompatibilitas ke belakang, menggunakan filter. Namun tetap saja, solusi ini berhasil.


46
Ini mungkin berhasil tetapi tiga loop untuk mencapai apa yang dapat dilakukan dalam satu baris kode menggunakan metode filter Array.
Joshaven Potter

9
Hanya untuk menjadi jelas, alat ini perbedaan simetris dari a1 dan a2 , tidak seperti jawaban yang lain diposting di sini.
200_sukses

25
Ini bukan jawaban terbaik, tapi saya memberikannya upvote amal, untuk membantu menebus downvotes yang tidak adil. Hanya jawaban yang salah yang harus diturunkan, dan jika saya sedang mengerjakan proyek dengan browser cruft dalam cakupan (masa sulit terjadi), jawaban ini bahkan mungkin bisa membantu.
Michael Scheper

3
Boleh saya tahu apa yang akan terjadi kapan var a1 = ['a', 'b'];dan var a2 = ['a', 'b', 'c', 'd', 'b'];, itu akan mengembalikan jawaban yang salah , yaitu ['c', 'd', 'b']bukannya ['c', 'd'].
skbly7

4
Cara tercepat adalah solusi yang paling naif. Saya menguji semua solusi yang diusulkan untuk perbedaan simetris di utas ini, dan pemenangnya adalah: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

1234

Ada cara yang lebih baik menggunakan ES7:


Persimpangan

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

Perbedaan titik-temu Diagram Venn

Untuk [1,2,3] [2,3]itu akan menghasilkan [2,3]. Di sisi lain, karena [1,2,3] [2,3,5]akan mengembalikan hal yang sama.


Perbedaan

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

Perbedaan kanan Diagram Venn

Untuk [1,2,3] [2,3]itu akan menghasilkan [1]. Di sisi lain, karena [1,2,3] [2,3,5]akan mengembalikan hal yang sama.


Untuk perbedaan simetris , Anda dapat melakukan:

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

Perbedaan simetris Venn Diagram

Dengan cara ini, Anda akan mendapatkan array yang berisi semua elemen arr1 yang tidak ada di arr2 dan sebaliknya

Seperti @Joshaven Potter menunjukkan jawabannya, Anda dapat menambahkan ini ke Array.prototype sehingga dapat digunakan seperti ini:

Array.prototype.diff = function(arr2) { return this.filter(x => !arr2.includes(x)); }
[1, 2, 3].diff([2, 3])

3
Saya lebih suka memeriksa < 0daripada== -1
Vic

1
Komputasi Arrayperbedaannya disebut set operation, karena pencarian properti adalah pekerjaan Sets sendiri, yang urutan besarnya lebih cepat dari indexOf/ includes. Sederhananya, solusi Anda sangat tidak efisien dan agak lambat.

@ftor tetapi dengan Set, nilai harus unik, bukan?
CervEd

1
@LuisSieira Saya mengerti bahwa itu akan berhasil karena [1,2,3] [2,3,5]angka-angkanya unik tetapi jika Anda mengatakan [1,1,2,3] [1,2,3,5]dan berharap [1]Anda tidak dapat menggunakannya Set. Solusi Anda tidak akan bekerja: - / Saya akhirnya membuat fungsi ini karena saya tidak bisa menemukan cara yang memuaskan untuk melakukannya dengan lebih ringkas. Jika Anda memiliki ide tentang cara melakukan itu, saya ingin tahu!
CervEd

3
Bukankah Array.includes()fitur ES7 bukan ES6? (1) (2) - dan untuk melanjutkan, dengan ES6 Anda dapat menggunakan Array.some()misalnya let intersection = aArray.filter(a => bArray.some(b => a === b)), bukan?
Jari Keinänen

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

Catatan indexOf dan filter tidak tersedia di ie sebelum ie9.


50
Satu-satunya browser yang penting yang tidak mendukung filter dan indexOf adalah IE8. IE9 mendukung keduanya. Jadi tidak salah.
Bryan Larsen

14
ie7 dan ie8 masih (sayangnya) sangat relevan, namun Anda dapat menemukan kode polyfill untuk kedua fungsi di situs MDN: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… developer.mozilla.org/en/JavaScript/ Referensi / Global_Objects / ... Muat dalam kode yang tercantum di bawah "kompatibilitas" melalui syarat & BOOM IE. Ie7 / 8 didukung.
1nfiniti

44
Solusi ini memiliki jangka waktu O (n ^ 2) solusi linier akan jauh lebih efisien.
jholloman

75
Jika Anda menggunakan fungsi seperti ini: [1,2,3].diff([3,4,5])ia akan kembali [1,2]sebagai ganti [1,2,4,5]sehingga tidak menyelesaikan masalah di pertanyaan awal, sesuatu yang harus diperhatikan.
Bugster

12
@AlinPurcaru Tidak didukung oleh browser kuno! = Salah. Mempertimbangkan Netscape 2.0, sebagian besar kode JS di sini "salah" dengan definisi ini. Mengatakan hal itu konyol.
NullUserException

304

Sejauh ini, ini adalah cara termudah untuk mendapatkan hasil yang Anda cari, menggunakan jQuery:

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

diffsekarang berisi apa yang ada di dalamnya old_arrayyang tidak ada dinew_array


4
@ Batman Ya, tetapi hanya jika mereka referensi ke objek yang sama ( {a: 1} != {a: 1}) ( bukti )
Matmarbon

Apakah mungkin untuk menggunakannya dengan array yang menyimpan data objek khusus? Saya mencobanya dengan cara yang sama tetapi tidak berhasil. Setiap ide akan sangat dihargai.
LetMeCodeAnda

8
Apakah ini tipuan? The doc menganggap metode ini sebagai bagian dari Metode Elemen DOM dan bukan sebagai array pembantu umum. Jadi mungkin berfungsi seperti ini sekarang, tetapi mungkin tidak dalam versi yang akan datang, karena tidak dimaksudkan untuk menggunakannya dengan cara ini. Meskipun, saya akan senang jika secara resmi akan menjadi pembantu array umum.
robsch

1
@robsch Saat Anda menggunakan .notarray, jQuery menggunakan utilitas bawaannya .grep()yang khusus untuk memfilter array. Saya tidak bisa melihat perubahan ini.
superphonic

1
@ vsync Kedengarannya seperti Anda mengejar perbedaan simetris
superfoni

158

Metode perbedaan di Underscore (atau penggantian drop-in, Lo-Dash ) dapat melakukan ini juga:

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

Seperti halnya fungsi Garis Bawah, Anda juga bisa menggunakannya dalam gaya yang lebih berorientasi objek:

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

4
Saya pikir ini solusi yang baik untuk kinerja, terutama karena lodash dan garis bawah terus berjuang untuk implementasi terbaik. Juga, ini kompatibel dengan IE6.
mahemoff

4
Hati-hati, implementasi ini tidak akan berfungsi untuk array objek. Lihat stackoverflow.com/q/8672383/14731 untuk informasi lebih lanjut.
Gili

1
Sebagai salah satu jawaban yang disebutkan di sana, itu berfungsi jika itu adalah objek yang sama, tetapi tidak jika dua objek memiliki sifat yang sama. Saya pikir tidak apa-apa karena pengertian kesetaraan berbeda-beda (misalnya, bisa juga menjadi atribut "id" di beberapa aplikasi). Namun, akan lebih baik jika Anda bisa lulus dalam tes perbandingan untuk memotong ().
mahemoff

Untuk anak cucu: Lodash sekarang memiliki _.differenceBy () yang membutuhkan panggilan balik untuk melakukan perbandingan; jika Anda membandingkan objek, Anda dapat memasukkan fungsi yang membandingkannya sesuka Anda.
SomeCallMeTim

2
Hati-hati jika urutan argumen dibalik, itu tidak akan berhasil. misalnya. _.difference ([5, 2, 10], [1, 2, 3, 4, 5]); tidak bisa mendapatkan perbedaan
Russj

79

JavaScript polos

Ada dua kemungkinan intepretasi untuk "perbedaan". Saya akan membiarkan Anda memilih yang mana yang Anda inginkan. Katakanlah Anda memiliki:

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. Jika Anda ingin mendapatkan ['a'], gunakan fungsi ini:

    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. Jika Anda ingin mendapatkan ['a', 'c'](semua elemen yang terkandung dalam salah satu a1 atau a2, tetapi tidak keduanya - yang disebut perbedaan simetris ), gunakan fungsi ini:

    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 / Garis Bawah

Jika Anda menggunakan lodash, Anda dapat menggunakan _.difference(a1, a2)(kasus 1 di atas) atau _.xor(a1, a2)(kasus 2).

Jika Anda menggunakan Underscore.js, Anda dapat menggunakan _.difference(a1, a2) fungsi untuk case 1.

ES6 Set, untuk array yang sangat besar

Kode di atas berfungsi di semua browser. Namun, untuk array besar lebih dari sekitar 10.000 item, itu menjadi sangat lambat, karena memiliki kompleksitas O (n²). Pada banyak browser modern, kita dapat memanfaatkan Setobjek ES6 untuk mempercepatnya. Lodash secara otomatis digunakan Setketika tersedia. Jika Anda tidak menggunakan lodash, gunakan implementasi berikut, yang terinspirasi oleh posting blog 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));
}

Catatan

Perilaku untuk semua contoh mungkin mengejutkan atau tidak jelas jika Anda peduli dengan -0, +0, NaN atau array jarang. (Untuk sebagian besar kegunaan, ini tidak masalah.)


Terima kasih kamu menyelamatkan hariku. Saya harus membandingkan array 300K, dan solusi "Set" Anda bekerja dengan sempurna. Ini harus menjadi jawaban yang diterima.
justadev

52

Untuk mendapatkan perbedaan simetris Anda perlu membandingkan array dalam kedua cara (atau dalam semua cara dalam kasus beberapa array)

masukkan deskripsi gambar di sini


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

Contoh:

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

Perbedaan antara Array Objek

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

Contoh:

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

51

Pendekatan yang lebih bersih dalam ES6 adalah solusi berikut.

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

Perbedaan

a2.filter(d => !a1.includes(d)) // gives ["c", "d"]

Persimpangan

a2.filter(d => a1.includes(d)) // gives ["a", "b"]

Disjunctive Union (Perbedaan Simetris)

[ ...a2.filter(d => !a1.includes(d)),
  ...a1.filter(d => !a2.includes(d)) ]

Ia hanya bekerja dalam satu arah. Sekarang bayangkan bahwa a1 = ['a', 'b', 'e']: e tidak akan diekstraksi.
imrok

ya, begitulah perbedaan dalam teori himpunan bekerja. (a2 -a1) yang Anda cari adalah (a2-a1) + (a1-a2)
ifelse.codes

1
@imrok Saya yakin ini yang Anda cari [... a2.filter (d =>! a1.includes (d)), ... (a1.filter (d =>! a2.includes (d)) )]
ifelse.codes

2
Solusi indah, terima kasih!
Thiago Alves

40

Anda dapat menggunakan Set dalam kasus ini. Ini dioptimalkan untuk jenis operasi ini (penyatuan, persimpangan, perbedaan).

Pastikan itu berlaku untuk kasus Anda, setelah tidak ada duplikat.

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
Itu terlihat seperti perpustakaan yang bagus! Sayang sekali Anda tidak dapat men-download hanya Setfungsi tanpa harus mendapatkan yang lain ...
Blixt

@Blixt Saya yakin Anda dapat mengunduh semuanya, dan cukup sertakan saja file set.js
Samuel Carrijo

Set diimplementasikan dalam penutupan google juga. closure-library.googlecode.com/svn/docs/…
Ben Flynn


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

Gabungkan kedua array, nilai unik hanya akan muncul sekali sehingga indexOf () akan sama dengan lastIndexOf ().


3
Saya setuju ini adalah cara paling bersih dan paling sederhana dan menyenangkan yang tidak memerlukan prototipe menyentuh. "Jika kamu tidak bisa menjelaskannya kepada anak enam tahun, kamu sendiri tidak mengerti." - Albert Einstein
lacostenycoder

18

untuk mengurangi satu array dari yang lain, cukup gunakan snippet di bawah ini:

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

Ini akan mengembalikan ['1,' 2 ',' 6 '] yang merupakan item dari larik pertama yang tidak ada di larik kedua.

Oleh karena itu, sesuai dengan sampel masalah Anda, kode berikut adalah solusi yang tepat:

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

Dengan kedatangan ES6 dengan set dan operator percikan (pada saat itu hanya berfungsi di Firefox, periksa tabel kompatibilitas ), Anda dapat menulis satu liner berikut:

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

yang akan menghasilkan [ "c", "d" ].


Hanya ingin tahu bagaimana bedanya dengan melakukanb.filter(x => !a.indexOf(x)))
chovy

1
@ Chovy berbeda dalam kompleksitas waktu. Solusi saya adalah solusi O(n + m)Anda di O(n * m)mana n dan m adalah panjang array. Buat daftar panjang dan solusi saya akan berjalan dalam hitungan detik, sedangkan milik Anda akan memakan waktu berjam-jam.
Salvador Dali

Bagaimana dengan membandingkan atribut dari daftar objek? Apakah mungkin menggunakan solusi ini?
chovy

2
a.filter(x => !b1.has(x))lebih sederhana. Dan perhatikan spec hanya membutuhkan kompleksitas yang akan n * f(m) + mdengan f(m)sublinear rata-rata. Ini lebih baik daripada n * m, tetapi tidak harus n + m.
Oriol

2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];Mengapa Anda membuat duplikat array 'a'? Mengapa Anda mengubah hasil filter menjadi satu set dan kemudian kembali ke array? Bukankah ini setara denganvar difference = a.filter(x => !b1.has(x));
Deepak Mittal

13

Pendekatan fungsional dengan ES2015

Komputasi differenceantara dua array adalah salah satu Setoperasi. Istilah sudah menunjukkan bahwa Setjenis asli harus digunakan, untuk meningkatkan kecepatan pencarian. Bagaimanapun, ada tiga permutasi ketika Anda menghitung perbedaan antara dua set:

[+left difference] [-intersection] [-right difference]
[-left difference] [-intersection] [+right difference]
[+left difference] [-intersection] [+right difference]

Berikut adalah solusi fungsional yang mencerminkan permutasi ini.

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

Benar difference:

differencersepele. Itu hanya differenceldengan argumen terbalik. Anda dapat menulis fungsi untuk kenyamanan:const differencer = flip(differencel) . Itu saja!

Simetris difference:

Sekarang kita memiliki yang kiri dan kanan, mengimplementasikan simetris differencemenjadi sepele juga:

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

Saya kira contoh ini adalah titik awal yang baik untuk mendapatkan kesan apa arti pemrograman fungsional:

Pemrograman dengan blok bangunan yang dapat dihubungkan bersama dengan berbagai cara.


12

Solusi menggunakan indexOf()akan baik untuk array kecil tetapi karena mereka tumbuh panjang kinerja pendekatan algoritma O(n^2). Berikut adalah solusi yang akan bekerja lebih baik untuk array yang sangat besar dengan menggunakan objek sebagai array asosiatif untuk menyimpan entri array sebagai kunci; itu juga menghilangkan entri duplikat secara otomatis tetapi hanya berfungsi dengan nilai string (atau nilai yang dapat disimpan dengan aman sebagai string):

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

Anda ingin menggunakan Object.hasOwnProperty () setiap kali Anda melakukan "untuk di" pada suatu objek. Kalau tidak, Anda menjalankan risiko perulangan melalui setiap bidang yang ditambahkan ke prototipe Obyek default. (Atau hanya orang tua objek Anda) Juga Anda hanya perlu dua loop, satu untuk pembuatan tabel hash, dan yang lainnya mencari di atas tabel hash.
jholloman

1
@ jholloman saya dengan hormat tidak setuju . Sekarang kita dapat mengontrol enumerabilitas untuk properti apa pun, mungkin Anda harus memasukkan properti apa pun yang Anda dapatkan selama enumerasi.
Phrogz

1
@ Phrogz Poin yang bagus jika Anda hanya khawatir tentang browser modern. Sayangnya saya harus mendukung kembali ke IE7 di tempat kerja sehingga zaman batu adalah kereta pemikiran default saya dan kami tidak cenderung menggunakan shims.
jholloman

10

Jawaban di atas oleh Joshaven Potter luar biasa. Tetapi mengembalikan elemen dalam array B yang tidak dalam array C, tetapi tidak sebaliknya. Misalnya, jika var a=[1,2,3,4,5,6].diff( [3,4,5,7]);kemudian akan menampilkan: ==> [1,2,6], tetapi tidak [1,2,6,7] , yang merupakan perbedaan aktual antara keduanya. Anda masih dapat menggunakan kode Potter di atas tetapi cukup ulangi perbandingannya sekali lagi juga:

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

Ini akan menghasilkan: [ 1, 2, 6, 7 ]


9

Cara lain untuk memecahkan masalah

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]

Anda juga dapat menggunakan sintaks fungsi panah:

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]

Anda seharusnya tidak pernah memperluas objek asli dengan cara ini. Jika standar memperkenalkan differencesebagai fungsi di versi masa depan dan fungsi ini kemudian memiliki fungsi tanda tangan yang berbeda dari Anda, itu akan merusak kode Anda atau pustaka asing yang menggunakan fungsi ini.
t.niese

7

Solusi Sangat Sederhana dengan fungsi filter 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

Bagaimana dengan ini:

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

Jadi cara ini dapat Anda lakukan array1.diff(array2)untuk mendapatkan perbedaannya (kompleksitas waktu yang mengerikan untuk algoritma - O (array1.length x array2.length) Saya percaya)


Menggunakan opsi filter adalah ide yang bagus ... namun, Anda tidak perlu membuat metode berisi untuk Array. Saya mengubah ide Anda menjadi satu kalimat ... Terima kasih atas ilhamnya!
Joshaven Potter

Anda tidak perlu mendefinisikan fungsi berisi (). JS meliputi () melakukan hal yang sama.
Da Man

4

Menggunakan http://phrogz.net/JS/ArraySetMath.js Anda dapat:

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

ini bekerja untuk saya


3
  • Solusi JavaScript murni (tanpa perpustakaan)
  • Kompatibel dengan browser lama (tidak digunakan filter )
  • O (n ^ 2)
  • fnParameter panggilan balik opsional yang memungkinkan Anda menentukan cara membandingkan item array

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(', ')
);


Anda dapat men-cache nilai panjang untuk memeras beberapa kecepatan lagi. Saya ingin merekomendasikan mengakses elemen array tanpa memeriksa panjang, tetapi ternyata pemeriksaan sederhana menghasilkan speedup hampir 100x.
Slotos

Tidak ada alasan untuk menyimpan lengthnilai. Ini sudah properti biasa. jsperf.com/array-length-caching
vp_arth

3

Ini berfungsi: pada dasarnya menggabungkan dua array, mencari duplikat dan mendorong apa yang tidak digandakan ke array baru yang merupakan perbedaan.

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

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

Kompleksitas simetris dan linier . Membutuhkan 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));
}


2

Hanya berpikir ... demi tantangan ;-) akankah ini berhasil ... (untuk array dasar string, angka, dll.) Tidak ada array bersarang

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

Perhatikan pengurutan kemungkinan tidak akan seperti yang disebutkan di atas ... tetapi jika diinginkan, panggil .sort () pada array untuk mengurutkannya.


2

Saya ingin fungsi serupa yang mengambil array lama dan array baru dan memberi saya array item yang ditambahkan dan array item yang dihapus, dan saya ingin itu menjadi efisien (jadi tidak ada .contains!).

Anda dapat bermain dengan solusi yang saya usulkan di sini: http://jsbin.com/osewu3/12 .

Adakah yang bisa melihat masalah / peningkatan pada algoritma itu? Terima kasih!

Daftar kode:

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

Saya mencari jawaban sederhana yang tidak melibatkan menggunakan perpustakaan yang berbeda, dan saya datang dengan saya sendiri yang saya pikir tidak disebutkan di sini. Saya tidak tahu seberapa efisien itu atau apa pun selain itu berfungsi;

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

Untuk kode saya, saya perlu duplikat juga diambil, tapi saya kira itu tidak selalu disukai.

Saya kira downside utama adalah itu berpotensi membandingkan banyak opsi yang sudah ditolak.


2

littlebit memperbaiki untuk jawaban terbaik

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

ini akan mempertimbangkan jenis elemen saat ini. b / c ketika kita membuat [a1 [i]] itu mengkonversi nilai menjadi string dari nilai aslinya, jadi kami kehilangan nilai aktual.


Ini masih gagal untuk berbagai objek. var a = [{a: 1}], b = [{b: 2}] arr_diff (a, b) == [].
EricP
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.