JavaScriptで2つの配列の違いを取得するにはどうすればよいですか?


753

JavaScriptの2つの配列の違いを返す方法はありますか?

例えば:

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

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

9
対称か非対称か?
オービット

新しいES6機能を使用すると、これを単純な1つのライナーとして実行できます(すべての主要なブラウザーで使用できるようになるには、かなりの時間がかかります)。とにかく私の答えを
サルバドールダリ

1
ソリューションの重要な側面はパフォーマンスです。このタイプの操作の漸近的な時間の複雑さ-他の言語では-はO(a1.length x log(a2.length))-このパフォーマンスはJavaScriptで可能ですか?
Raul

回答:


219

通常のアレイを比較していると思います。そうでない場合は、forループをfor .. inループに変更する必要があります。

function arr_diff (a1, a2) {

    var a = [], diff = [];

    for (var i = 0; i < a1.length; i++) {
        a[a1[i]] = true;
    }

    for (var i = 0; i < a2.length; i++) {
        if (a[a2[i]]) {
            delete a[a2[i]];
        } else {
            a[a2[i]] = true;
        }
    }

    for (var k in a) {
        diff.push(k);
    }

    return diff;
}

console.log(arr_diff(['a', 'b'], ['a', 'b', 'c', 'd']));
console.log(arr_diff("abcd", "abcde"));
console.log(arr_diff("zxc", "zxc"));

下位互換性を気にしない場合のより良い解決策は、フィルターを使用することです。しかし、それでも、このソリューションは機能します。


46
これは機能するかもしれませんが、Arrayのfilterメソッドを使用して1行のコードで実行できることを達成するために3つのループを実行します。
Joshaven Potter、

9
明確にするために、これはa1a2の対称的な違いを実装しています。
200_成功

25
これは最良の答えではありませんが、不当な反対投票を埋め合わせるために、慈善投票を差し上げています。間違った回答のみを反対投票する必要があります。私がクルーフブラウザーを対象とするプロジェクトで作業している場合(かなりの時間が発生します)、この回答は役立つ場合もあります。
Michael Scheper、2015年

3
私は、ときに何が起こるか知っているかもしれないvar a1 = ['a', 'b'];var a2 = ['a', 'b', 'c', 'd', 'b'];それが間違った答えを返します、つまり['c', 'd', 'b']代わりに['c', 'd']
skbly7 2015年

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

1233

ES7を使用するより良い方法があります。


交差点

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

交差差ベン図

[1,2,3] [2,3]それが得られます[2,3]。一方、for [1,2,3] [2,3,5]は同じものを返します。


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

右差ベン図

[1,2,3] [2,3]それが得られます[1]。一方、for [1,2,3] [2,3,5]は同じものを返します。


対称差、あなたが行うことができます。

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

対称差ベン図

このようにして、arr2に存在しないarr1のすべての要素を含む配列を取得します。

@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
Vic

1
プロパティのルックアップはsの非常に独自の仕事であり、/ よりも桁違いに速いArrayため、違いの計算はいわゆるです。簡単に言えば、あなたのソリューションは非常に非効率的で、かなり遅いです。set operationSetindexOfincludes

@ftorですがSet、値は一意である必要がありますか?
CervEd 2016年

1
@LuisSieira [1,2,3] [2,3,5]数字が一意であることを考えると、うまくいくと思いますが、あなたが言っていて[1,1,2,3] [1,2,3,5]、期待し[1]ていたなら、使用できませんでしたSet。しかし、あなたの解決策も機能しません:-/ もっと簡潔にそれを行うための満足のいく方法を理解できなかったので、私はこの関数を作成することになりました。あなたがそれを行う方法について何かアイデアがあれば、私は知りたいです!
CervEd 2016年

3
Array.includes()ES6ではなくES7機能ではありませんか?(1) (2) —そして、続行するには、ES6で、Array.some()たとえば let intersection = aArray.filter(a => bArray.some(b => a === b))、使用できますか?
JariKeinä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"]

のindexOfとフィルタはIE9の前にIEで利用できません。


50
フィルターとindexOfをサポートしない重要なブラウザーはIE8だけです。IE9は両方をサポートします。だからそれは間違いではありません。
ブライアンラーセン、

14
ie7とie8はまだ(残念ながら)非常に関連がありますが、MDNサイトのdeveloper.mozilla.org/en/JavaScript/Reference/Global_Objects/…で両方の関数のポリフィルコードを見つけることができます。 developer.mozilla.org/en/JavaScript/ Reference / Global_Objects /… IEの条件付き&BOOMを介して、「互換性」の下にリストされているコードをロードします。IE7 / 8がサポートされています。
1nfiniti 2012年

44
このソリューションの実行時間はO(n ^ 2)であり、線形ソリューションの方がはるかに効率的です。
jholloman 2012

75
次のような関数を使用すると、元の質問の問題が解決されないため、代わりに[1,2,3].diff([3,4,5])返されます。[1,2][1,2,4,5]
バグスター

12
@AlinPurcaru古いブラウザではサポートされていません!=間違っています。Netscape 2.0を考えると、ここでのJSコードのほとんどは、この定義では「間違っています」。それは愚かなことです。
NullUserException 2013年

304

これは、jQueryを使用して、探している結果を正確に得る最も簡単な方法です。

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

diff今では含まれold_arrayていないものが含まれていますnew_array


4
@バットマンはい、ただしそれらが同じオブジェクトへの参照である場合のみ({a: 1} != {a: 1})(証明
Matmarbon

カスタムオブジェクトのデータを保持する配列でそれを使用することは可能ですか?実際に同じ方法で試しましたが、うまくいきませんでした。どんなアイディアも高く評価されます。
LetMeCodeYou 2015年

8
これはトリックですか?このドキュメントは、このメソッドをDOM要素メソッドの一部と見なし、一般的な配列ヘルパーとは見なしていません。したがって、現在はこのように機能する可能性がありますが、この方法で使用することを意図していないため、将来のバージョンではおそらく機能しない可能性があります。しかし、それが正式に一般的な配列ヘルパーになれば幸いです。
robsch

1
@robsch .not配列で使用する場合、jQueryは.grep()配列をフィルタリングするための組み込みユーティリティを使用します。私はこの変化を見ることができません。
スーパーフォニック

1
@vsync対称的な違いの後のように聞こえる
スーパーフォニック

158

Underscoreの差分メソッド(またはそのドロップイン置換、Lo-Dash)でもこれを行うことができます。

(R)eturns the values from array that are not present in the other arrays

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

他のアンダースコア関数と同様に、よりオブジェクト指向のスタイルで使用することもできます。

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

4
特にlodashとunderscoreが最良の実装を求めて戦っているので、これはパフォーマンスの点で優れたソリューションだと思います。また、IE6互換です。
mahemoff 2012

4
この実装はオブジェクトの配列に対しては機能しないことに注意してください。詳細については、stackoverflow.com / q / 8672383/14731を参照してください。
ギリ

1
回答の1つで言及されているように、同じオブジェクトの場合は機能しますが、2つのオブジェクトのプロパティが同じ場合は機能しません。平等の概念はさまざまなので(たとえば、一部のアプリでは「id」属性の場合もある)、これは問題ないと思います。ただし、intersect()に比較テストを渡せればよいでしょう。
mahemoff 2013年

後世のために:Lodashに_.differenceBy()が追加され、比較を行うためにコールバックを受け取ります。オブジェクトを比較している場合は、必要に応じてオブジェクトを比較する関数をドロップできます。
SomeCallMeTim

2
引数の順序を逆にすると、機能しなくなりますので注意してください。例えば。_.difference([5、2、10]、[1、2、3、4、5]); diffを取得できません
Russj

79

プレーンJavaScript

「違い」には2つの解釈があります。どれにするかはお任せします。あなたが持っているとしましょう:

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'](すべての要素がまたはに含まれます が、両方には含まれません-いわゆる対称差)、次の関数を使用します。a1a2

    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²)の複雑さがあるため、処理速度はかなり遅くなります。最近の多くのブラウザでは、ES6 Setオブジェクトを利用して高速化できます。LodashはSet、利用可能になると自動的に使用します。lodashを使用していない場合は、Axel Rauschmayerのブログ投稿に触発された次の実装を使用してください。

function difference(a1, a2) {
  var a2Set = new Set(a2);
  return a1.filter(function(x) { return !a2Set.has(x); });
}

function symmetricDifference(a1, a2) {
  return difference(a1, a2).concat(difference(a2, a1));
}

ノート

すべての例の動作は、-0、+ 0、NaN、またはスパース配列に関心がある場合、意外な場合もあれば、明白でない場合もあります。(ほとんどの場合、これは問題ではありません。)


感謝。あなたは私の日を救った。300Kアレイを比較する必要があり、「セット」ソリューションは完全に機能しました。これは受け入れられる答えになるはずです。
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}]

51

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は抽出されません。
imrok

はい、それが集合論の違いが機能する方法です。(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ファイルだけを含めることができると思います
Samuel Carrijo

セットは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
これが最もクリーンでシンプルな方法であり、プロトタイプに手を加える必要がないという点で同意します。「6歳の子供に説明できないと、自分では理解できません。」―アルバートアインシュタイン
lacostenycoder、2015年

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

2番目の配列には存在しない最初の配列の項目である['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でのみ動作しているときは、互換性の表を確認してください)、次の1つのライナーを作成できます。

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)))
チョービー2015年

1
@chovyそれは時間の複雑さが異なります。私の解決O(n + m)策はO(n * m)、nとmが配列の長さであるソリューションです。長いリストを取ると、私のソリューションは数秒で実行されますが、あなたのリストは数時間かかります。
Salvador Dali

オブジェクトのリストの属性を比較するのはどうですか?このソリューションを使用してそれは可能ですか?
2015年

2
a.filter(x => !b1.has(x))簡単です。また、仕様では、複雑さが平均n * f(m) + mしてf(m)サブリニアであることだけが必要であることに注意してください。はより優れていますがn * m、必ずしもそうとは限りませんn + m
Oriol

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での機能的アプローチ

difference2つの配列間の計算はSet操作の1つです。この用語はすでにSet、検索速度を上げるためにネイティブタイプを使用する必要があることを示しています。とにかく、2つのセットの差を計算する場合、3つの順列があります。

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

オブジェクトで「for in」を実行しているときはいつでも、Object.hasOwnProperty()を使用したいとします。そうしないと、デフォルトのオブジェクトのプロトタイプに追加されたすべてのフィールドをループするリスクがあります。(またはオブジェクトの親のみ)また、必要なループは2つだけです。1つはハッシュテーブルの作成用で、もう1つはそのハッシュテーブルを検索します。
jholloman 2012

1
@jholloman私は敬意表しません。これで、任意のプロパティの列挙可能性を制御できるようになりました。おそらく、列挙中に取得するすべてのプロパティを含める必要があります。
Phrogz 2013年

1
@Phrogz最近のブラウザだけを心配している場合は良い点です。残念ながら、私は職場でIE7に戻る必要があるので、石器時代は私のデフォルトの考えであり、シムを使用する傾向はありません。
jholloman 2013年

10

Joshaven Potterによる上記の答えは素晴らしいです。しかし、それは配列Cにない配列Bの要素を返しますが、その逆は返しません。もし、例えばvar a=[1,2,3,4,5,6].diff( [3,4,5,7]);==>:それが出力する[1,2,6]が、しない [1,2,6,7]二つの間の実際の差です。上記のPotterのコードを使用することもできますが、単純に逆方向に比較をやり直すこともできます。

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)と思います)


フィルターオプションを使用するのは良い考えです...しかし、配列のcontainsメソッドを作成する必要はありません。あなたのアイデアをワンライナーに変えました...インスピレーションをありがとう!
Joshaven Potter、

contains()関数を定義する必要はありません。JSのincludes()は同じことを行います。
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倍のスピードアップが得られたようです。
Slotos、2014年

length値をキャッシュする理由はありません。それはすでに明白な財産です。jsperf.com/array-length-caching
vp_arth

3

これは機能しています。基本的に2つの配列をマージし、重複を探して、重複していないものを新しい配列にプッシュします。これが違いです。

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

古い配列と新しい配列を取り込んで、追加された項目の配列と削除された項目の配列を提供する同様の関数が必要でした。また、効率的なものにしたいので(.contains!はありません)。

ここで私の提案するソリューションを試すことができます:http : //jsbin.com/osewu3/12

誰かがそのアルゴリズムの問​​題/改善を見ることができますか?ありがとう!

コードリスト:

function diff(o, n) {
  // deal with empty lists
  if (o == undefined) o = [];
  if (n == undefined) n = [];

  // sort both arrays (or this won't work)
  o.sort(); n.sort();

  // don't compare if either list is empty
  if (o.length == 0 || n.length == 0) return {added: n, removed: o};

  // declare temporary variables
  var op = 0; var np = 0;
  var a = []; var r = [];

  // compare arrays and add to add or remove lists
  while (op < o.length && np < n.length) {
      if (o[op] < n[np]) {
          // push to diff?
          r.push(o[op]);
          op++;
      }
      else if (o[op] > n[np]) {
          // push to diff?
          a.push(n[np]);
          np++;
      }
      else {
          op++;np++;
      }
  }

  // add remaining items
  if( np < n.length )
    a = a.concat(n.slice(np, n.length));
  if( op < o.length )
    r = r.concat(o.slice(op, o.length));

  return {added: a, removed: r}; 
}

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

最良の答えのためのlittlebit修正

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でa [a1 [i]]を作成すると、値が元の値から文字列に変換されるため、実際の値は失われます。


これは、オブジェクトの配列ではまだ失敗します。var a = [{a:1}]、b = [{b:2}] arr_diff(a、b)== []。
EricP 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.