JavaScript'te iki dizi arasındaki fark nasıl elde edilir?


752

JavaScript'te iki dizi arasındaki farkı döndürmenin bir yolu var mı?

Örneğin:

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

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

9
Simetrik mi simetrik değil mi?
Yörüngedeki Hafiflik Yarışları

Yeni ES6 fonksiyonu ile bu basit bir astar olarak yapılabilir (tüm büyük tarayıcılarda kullanmak çok zaman alacaktır). Her durumda cevabımı
Salvador Dali

1
çözümün önemli bir yönü performanstır. bu tür bir işlemin asimptotik zaman karmaşıklığı - diğer dillerde - O(a1.length x log(a2.length))bu performans JavaScript'te mümkün müdür?
Raul

Yanıtlar:


218

Sanırım normal bir diziyi karşılaştırıyorsunuz. Değilse, for döngüsünü for .. in loop olarak değiştirmeniz gerekir .

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

Daha iyi bir çözüm, geriye dönük uyumlulukla ilgilenmiyorsanız, filtre kullanmaktır. Ama yine de, bu çözüm işe yarıyor.


46
Bu işe yarayabilir, ancak Array'ın filtre yöntemini kullanarak bir kod satırında neler yapılabileceğini başarmak için üç döngü yapar.
Joshaven Potter

9
Daha açıkçası, bu uygular simetrik fark ait a1 ve a2 diğer cevaplar aksine burada yayınlanan.
200_success

25
Bu en iyi cevap değil, ama haksız downvotları telafi etmeye yardımcı olmak için bir hayır kurumu açıyorum. Sadece yanlış cevaplar indirilmemelidir ve eğer ham tarayıcılar kapsamında bir proje üzerinde çalışsaydım (zor zamanlar olur), bu cevap bile yardımcı olabilir.
Michael Scheper

3
Ben ne olacağını biliyor olabilir var a1 = ['a', 'b'];ve var a2 = ['a', 'b', 'c', 'd', 'b'];, bunun yanlış cevap dönecektir yani ['c', 'd', 'b']yerine ['c', 'd'].
skbly7

4
En hızlı yol en açık çözümdür. Ben bu iş parçacığı simetrik fark için önerilen tüm çözümleri test ve kazanan: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

1231

ES7 kullanmanın daha iyi bir yolu var:


kesişim

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

Kavşak farkı Venn Şeması

Bunun için [1,2,3] [2,3]verim olacaktır [2,3]. Öte yandan, [1,2,3] [2,3,5]aynı şeyi geri getirecektir.


fark

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

Doğru fark Venn Şeması

Bunun için [1,2,3] [2,3]verim olacaktır [1]. Öte yandan, [1,2,3] [2,3,5]aynı şeyi geri getirecektir.


Bir İçin simetrik fark , bunu yapabilirsiniz:

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

Simetrik fark Venn Şeması

Bu şekilde, arr1 öğesinde olmayan arr1 öğelerinin tüm öğelerini içeren bir dizi alırsınız.

@Joshaven Potter'ın cevabına işaret ettiği gibi, bunu Array.prototype'e ekleyebilirsiniz, böylece şu şekilde kullanılabilir:

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

3
Ben kontrol tercih < 0yerine== -1
Vic

1
Bilgisayar Arrayfark bir sözde olduğu set operationÖzellik arama çok kendi iştir çünkü, Setdaha hızlı daha sonra büyüklüklerdir s, indexOf/ includes. Basitçe söylemek gerekirse, çözümünüz çok verimsiz ve oldukça yavaş.

@ftor ama ile Setdeğerler benzersiz olmak zorunda, değil mi?
CervEd

1
@LuisSieira [1,2,3] [2,3,5]Numaraların benzersiz olduğu göz önüne alındığında işe yarayacağını anladım , ancak söylemiş [1,1,2,3] [1,2,3,5]ve beklemiş [1]olsaydınız kullanamazsınız Set. Çözümünüz de işe yaramayacaktı: - / Bu işlevi yarattım, çünkü daha özlü bir şekilde yapmanın tatmin edici bir yolunu bulamadım. Bunun nasıl yapılacağı hakkında herhangi bir fikriniz varsa, bilmek isterim!
CervEd

3
Is not Array.includes()yerine ES6 ait ES7 özelliği? (1) (2) - ve devam etmek için ES6 ile Array.some()örneğin kullanabilirsiniz let intersection = aArray.filter(a => bArray.some(b => a === b)), hayır?
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"]

Not indexOf ve filtre, yani ie9'dan önce kullanılamaz.


50
Filtre ve indexOf'u desteklemeyen önemli olan tek tarayıcı IE8'dir. IE9 her ikisini de destekler. Yani yanlış değil.
Bryan Larsen

14
ie7 ve ie8 hala (maalesef) çok alakalı, ancak MDN sitesinde her iki işlev için çoklu dolgu kodunu bulabilirsiniz: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… developer.mozilla.org/en/JavaScript/ Başvuru / Global_Objects /… IE koşullu ve BOOM aracılığıyla "uyumluluk" altında listelenen kodu yükleyin. IE7 / 8 desteklenmektedir.
1nfiniti

44
Bu çözeltinin çalışma süresi O (n ^ 2) olup, doğrusal bir çözüm çok daha verimli olacaktır.
jholloman

75
Böyle işlevini kullanıyorsanız: [1,2,3].diff([3,4,5])o dönecektir [1,2]yerine [1,2,4,5]orijinal söz konusu sorunu çözmezse, böylece bir şey farkında olmak.
Bugster

12
@AlinPurcaru Arkaik tarayıcılar tarafından desteklenmiyor! = Yanlış. Netscape 2.0 göz önüne alındığında, buradaki JS kodunun çoğu bu tanım tarafından "yanlış" tır. Söylemek aptalca bir şey.
NullUserException

304

Bu, jQuery kullanarak tam olarak aradığınız sonucu almanın en kolay yoludur:

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

diffşimdi ne içeriyor old_arrayo değilnew_array


4
@Batman Evet, ancak yalnızca aynı nesneye referanslarsa ( {a: 1} != {a: 1}) ( kanıt )
Matmarbon

Özel bir nesnenin verilerini tutan dizilerle kullanmak mümkün müdür? Aslında aynı şekilde denedim ama işe yaramadı. Herhangi bir fikir çok takdir edilecektir.
LetMeCodeYou

8
Bu bir hile mi? Doktor bir parçası olarak bu yöntemi dikkate DOM elemanlar metodu olup genel dizi yardımcı olarak. Bu yüzden şimdi bu şekilde çalışabilir, ancak belki de bu şekilde kullanılması amaçlanmadığı için gelecekteki sürümlerde olmayabilir. Rağmen, resmi olarak genel bir dizi yardımcı olacağını mutlu olurdu.
robsch

1
@robsch Bir .notdiziyle kullandığınızda, jQuery, .grep()dizileri filtrelemek için özel olarak yerleşik yardımcı programını kullanır . Bunun değiştiğini göremiyorum.
superphonic

1
@vsync Simetrik bir farkın peşindesiniz gibi geliyor
superphonic

158

Alt çizgi'deki fark yöntemi (veya onun yerine bırakılan Lo-Dash ) bunu da yapabilir:

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

Herhangi bir Alt Çizgi işlevinde olduğu gibi, bunu daha nesne yönelimli bir tarzda da kullanabilirsiniz:

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

4
Performans açısından iyi bir çözüm olduğunu düşünüyorum, özellikle lodash ve alt çizgi en iyi uygulama için mücadele etmeye devam ediyor. Ayrıca IE6 uyumludur.
mahemoff

4
Dikkat, bu uygulama nesnelerin dizileri için çalışmaz. Daha fazla bilgi için stackoverflow.com/q/8672383/14731 adresine bakın .
Gili

1
Yanıtlardan biri olarak belirtildiği gibi, aynı nesne ise işe yarar, ancak iki nesne aynı özelliklere sahipse işe yaramaz. Eşitlik kavramları değiştikçe bunun iyi olduğunu düşünüyorum (örneğin, bazı uygulamalarda "id" özelliği de olabilir). Ancak, intersect () için bir karşılaştırma testinden geçmeniz iyi olur.
mahemoff

Posterity için: Lodash şimdi karşılaştırmayı yapmak için bir geri arama alan _.differenceBy () işlevine sahiptir; nesneleri karşılaştırıyorsanız, ihtiyacınız olan her şeyi karşılaştıran bir işleve bırakabilirsiniz.
SomeCallMeTim

2
Argümanların sırası tersine çevrilirse, işe yaramaz. Örneğin. _ fark ([5, 2, 10], [1, 2, 3, 4, 5]); fark alamadım
Russj

79

Düz JavaScript

"Fark" için iki olası yorum vardır. Hangisini istediğini seçmene izin vereceğim. Diyelim ki:

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. Almak istiyorsanız ['a'], bu işlevi kullanın:

    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. Eğer almak istiyorsanız ['a', 'c'](tüm unsurları içerdiği ya a1 ya a2, ancak ikisini - sözde simetrik fark ), bu işlevi kullanın:

    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 / Alt Çizgi

Lodash kullanıyorsanız _.difference(a1, a2)(yukarıdaki 1. durum) veya _.xor(a1, a2)(2. durum) kullanabilirsiniz.

Underscore.js kullanıyorsanız, _.difference(a1, a2) durum 1'in işlevini .

ES6 Set, çok büyük diziler için

Yukarıdaki kod tüm tarayıcılarda çalışır. Bununla birlikte, yaklaşık 10.000'den fazla öğeden oluşan büyük diziler için, oldukça yavaş olur, çünkü O (n²) karmaşıklığına sahiptir. Birçok modern tarayıcıda, Setişleri hızlandırmak için ES6 nesnesinden yararlanabiliriz. Lodash Setmevcut olduğunda otomatik olarak kullanılır. Lodash kullanmıyorsanız, Axel Rauschmayer'in blog gönderisinden esinlenerek aşağıdaki uygulamayı kullanın :

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

notlar

-0, +0, NaN veya seyrek dizileri önemsiyorsanız tüm örneklerin davranışı şaşırtıcı veya açık olmayabilir . (Çoğu kullanım için bu önemli değil.)


Teşekkür. günümü kurtardın. 300K'lık bir diziyi karşılaştırmak zorunda kaldım ve "Set" çözümünüz mükemmel çalıştı. Bu kabul edilen cevap olmalı.
justadev

52

Simetrik farkı elde etmek için dizileri her iki şekilde (veya birden çok dizi olması durumunda tüm yollarla) karşılaştırmanız gerekir.

resim açıklamasını buraya girin


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

Misal:

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

Nesne Dizileri Arasındaki Fark

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

Misal:

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'da daha temiz bir yaklaşım aşağıdaki çözümdür.

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

fark

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

kesişim

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

Ayrık Birlik (Simetrik Fark)

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

Sadece bir yönde çalışır. Şimdi şunu hayal edin a1 = ['a', 'b', 'e']: e çıkartılmayacak.
imrok

evet, set teorisindeki fark bu şekilde işliyor. (a2 -a1) aradığınız şey (a2-a1) + (a1-a2)
ifelse.codes

1
@imrok Bu aradığınız şey olduğuna inanıyorum [... a2.filter (d =>! a1.includes (d)), ... (a1.filter (d =>! a2.includes (d)) )]
ifelse.codes

2
Güzel çözüm, teşekkürler!
Thiago Alves

40

Bu durumda bir Set kullanabilirsiniz . Bu tür bir işlem için optimize edilmiştir (birleşim, kavşak, fark).

Yinelemelere izin vermediğinde, durumunuz için geçerli olduğundan emin olun.

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
Güzel bir kütüphane gibi görünüyor! SetHer şeyi almak zorunda kalmadan sadece işlevi indiremediğiniz ne yazık !
Blixt

@Blixt Her şeyi indirebileceğinize ve sadece set.js dosyasını dahil edebileceğinize inanıyorum
Samuel Carrijo

Set de Google kapanışında uygulanmaktadır. 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);
  });
}

Her iki diziyi birleştirdiğinizde, benzersiz değerler yalnızca bir kez görünür, böylece indexOf (), lastIndexOf () ile aynı olur.


3
Bu prototip dokunmadan gerektirdiği en temiz ve basit yolu ve hoş olduğunu kabul ediyorum. “Eğer altı yaşında bir çocuğa açıklayamazsan, bunu kendin anlamıyorsun.” - Albert Einstein
lacostenycoder

18

bir diziyi diğerinden çıkarmak için aşağıdaki snippet'i kullanmanız yeterlidir:

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

İkincisi olmayan ilk dizinin öğeleri olan ['1,' 2 ',' 6 '] değerini döndürür.

Bu nedenle, sorun örneğinize göre, aşağıdaki kod tam çözümdür:

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'nın setler ve uyarıcı operatörü ile gelmesiyle (sadece Firefox'ta çalışırken, uyumluluk tablosunu kontrol edin ), aşağıdaki bir astarı yazabilirsiniz:

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

sonuçlanacak [ "c", "d" ].


Sadece merak nasıl yapmaktan farklı olduğunub.filter(x => !a.indexOf(x)))
chovy

1
@ chovy zaman karmaşıklığı farklıdır. Benim çözümüm, n ve m dizilerinin uzunlukları olduğu O(n + m)çözümdür O(n * m). Uzun listeler al ve çözümüm saniyeler içinde çalışacak, seninki ise saatler sürecek.
Salvador Dali

Nesneler listesinin bir özniteliğini karşılaştırmaya ne dersiniz? Bu çözümü kullanmak mümkün mü?
chovy

2
a.filter(x => !b1.has(x))daha basit. Ve spesifikasyonun sadece karmaşıklığın ortalama n * f(m) + molarak alt f(m)doğrusal ile olmasını gerektirdiğini unutmayın . Bundan daha iyi n * m, ama ille de değil n + m.
Oriol

2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];Neden yinelenen 'a' dizisi oluşturuyorsunuz? Neden filtrenin sonucunu bir kümeye, sonra tekrar diziye dönüştürüyorsunuz? Bu eşdeğer değilvar difference = a.filter(x => !b1.has(x));
Deepak Mittal

13

ES2015 ile fonksiyonel yaklaşım

Bilgisayar differencearasında iki diziler biridir Setoperasyonlar. Terim Set, arama hızını artırmak için yerel türün kullanılması gerektiğini zaten belirtir . Her neyse, iki set arasındaki farkı hesapladığınızda üç permütasyon vardır:

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

İşte bu permütasyonları yansıtan fonksiyonel bir çözüm.

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

Doğru difference:

differencerönemsizdir. Sadece differencelters çevrilmiş argümanlarla. Kolaylık sağlamak için bir işlev yazabilirsiniz:const differencer = flip(differencel) . Bu kadar!

Simetrik difference:

Şimdi sol ve sağ differenceolana sahibiz, simetrik uygulamak da önemsiz:

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

Sanırım bu örnek, fonksiyonel programlamanın ne anlama geldiğini görmek için iyi bir başlangıç ​​noktasıdır:

Birçok farklı şekilde birbirine takılabilen yapı taşlarıyla programlama.


12

indexOf()Küçük diziler için bir çözüm uygun olacaktır, ancak uzunlukları büyüdükçe algoritmanın performansı yaklaşmaktadır O(n^2). Dizi girişlerini anahtar olarak saklamak için ilişkilendirilebilir diziler olarak nesneleri kullanarak çok büyük diziler için daha iyi performans gösteren bir çözüm; yinelenen girişleri de otomatik olarak ortadan kaldırır, ancak yalnızca dize değerleriyle (veya dize olarak güvenli bir şekilde saklanabilen değerlerle) çalışır:

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

Bir nesnede "içinizde" yaptığınızda, Object.hasOwnProperty () öğesini kullanmak istersiniz. Aksi takdirde, varsayılan Nesnenin prototipine eklenen her alanda döngü riski taşırsınız. (Veya yalnızca nesnenizin üst öğesi) Ayrıca, biri karma tablo oluşturmak için diğeri de bu karma tabloya bakmak için yalnızca iki döngüye ihtiyacınız vardır.
jholloman

1
@jholloman Ben saygıyla katılmıyorum . Artık herhangi bir mülk için numaralandırmayı kontrol edebildiğimize göre, büyük olasılıkla numaralandırma sırasında aldığınız herhangi bir mülkü dahil etmelisiniz .
Phrogz

1
@Phrogz Yalnızca modern tarayıcılardan endişe ediyorsanız iyi bir nokta. Ne yazık ki taş devri benim varsayılan düşünce trenim ve şimler kullanma eğiliminde olmadığımız için işte IE7'ye destek vermem gerekiyor.
jholloman

10

Joshaven Potter'ın yukarıdaki cevabı harika. Ancak, B dizisindeki C dizisinde olmayan, ancak tersi olmayan öğeleri döndürür. Örneğin, var a=[1,2,3,4,5,6].diff( [3,4,5,7]);o zaman çıktı alır: ==> [1,2,6], ama değil [1,2,6,7] , bu ikisi arasındaki gerçek fark. Yine de yukarıda Potter'ın kodunu kullanabilirsiniz, ancak karşılaştırmayı bir kez daha geriye doğru tekrarlayabilirsiniz:

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

Bu çıktı: [ 1, 2, 6, 7 ]


9

Sorunu çözmenin başka bir yolu

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]

Ayrıca, ok işlevi sözdizimini kullanabilirsiniz:

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]

Yerel bir nesneyi asla bu şekilde genişletmemelisiniz. Standart, differencegelecekteki bir sürümde işlev olarak tanıtılırsa ve bu işlev sizinkinden farklı bir işlev imzasına sahipse, bu işlevi kullanan kodunuzu veya yabancı kitaplıkları bozacaktır.
t.niese

7

JavaScript'in filtre işleviyle Çok Basit Çözüm:

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

Buna ne dersin:

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

Böylece bu şekilde array1.diff(array2)farklarını elde etmek için yapabilirsiniz (Algoritmanın korkunç zaman karmaşıklığı olsa - O (dizi1.length x dizi2.length) inanıyorum)


Filtre seçeneğini kullanmak harika bir fikir ... ancak Array için bir içerme yöntemi oluşturmanız gerekmez. Fikrinizi tek bir astara dönüştürdüm ... İlhamınız için teşekkürler!
Joshaven Potter

İnclude () işlevini tanımlamanız gerekmez. JS include () aynı şeyi yapar.
Da Man

4

Http://phrogz.net/JS/ArraySetMath.js kullanarak şunları yapabilirsiniz:

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

bu benim için çalışıyor


3
  • Saf JavaScript çözümü (kitaplık yok)
  • Eski tarayıcılarla uyumludur (kullanmaz filter)
  • O (n ^ 2)
  • fnDizi öğelerinin nasıl karşılaştırılacağını belirlemenizi sağlayan isteğe bağlı geri arama parametresi

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


Biraz daha hız elde etmek için uzunluk değerlerini önbelleğe alabilirsiniz. Uzunluğu kontrol etmeden dizi öğelerine erişmeyi tavsiye etmek istedim, ancak görünüşe göre bu basit kontrol neredeyse 100x hız veriyor.
Slotos

Önbellek lengthdeğerlerine gerek yok. Zaten düz mülkiyet. jsperf.com/array-length-caching
vp_arth

3

Bu işe yarıyor: temel olarak iki diziyi birleştirin, yinelenenleri arayın ve çoğaltılamayanı fark olan yeni bir diziye itin.

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 yaklaşımı

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

Simetrik ve doğrusal karmaşıklık . ES6 gerektirir.

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

Sadece düşünmek ... bir meydan okuma uğruna ;-) bu işe yarayacaktır ... (dize, sayı vb. Temel diziler için) iç içe diziler yok

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

Sıralama büyük olasılıkla yukarıda belirtildiği gibi olmayacaktır ... ancak istenirse diziyi sıralamak için .sort () yöntemini çağırın.


2

Eski bir dizi ve yeni bir dizi aldı ve bana bir dizi öğe ve kaldırılan öğelerin bir dizi verdi benzer bir işlev istedim ve verimli olmasını istedim (yani hiçbir .contains!).

Önerilen çözümümle buradan oynayabilirsiniz: http://jsbin.com/osewu3/12 .

Bu algoritmada herhangi bir sorun / iyileştirme görebilen var mı? Teşekkürler!

Kod listesi:

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

Farklı kütüphanelerin kullanılmasını gerektirmeyen basit bir cevap arıyordum ve burada bahsetmediğimi düşündüğüm bir cevap buldum. Ne kadar verimli veya hiçbir şey bilmiyorum ama işe yarıyor;

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

Kodum için de çıkarılmış kopyaları gerekir, ama sanırım bu her zaman tercih edilmez.

Sanırım ana dezavantajı, potansiyel olarak zaten reddedilen birçok seçeneği karşılaştırıyor olmasıdır.


2

en iyi cevap için küçük bit düzeltme

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

bu, geçerli eleman tipini dikkate alacaktır. b / c [a1 [i]] yaptığımızda, bir değeri orojinal değerinden dizeye dönüştürür, böylece gerçek değeri kaybettik.


Bu hala bir dizi nesne için başarısız oluyor. var a = [{a: 1}], b = [{b: 2}] arr_diff (a, b) == [].
EricP
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.