JavaScriptの配列交差の最も単純なコード


610

JavaScriptで配列の交差を実装するための最も簡単なライブラリフリーのコードは何ですか?書きたい

intersection([1,2,3], [2,3,4,5])

そして得る

[2, 3]

16
シンプルにするか高速にするか?
SLaks、2009

11
優先順位は単純ですが、それがパフォーマンスの独占になるほど脳死になることはできません:)
Peter

4
ここでは、 _underscore交差関数を含むすべてのメソッドの JsFiddle Banchmarkテストページを作成しました。(高いほど良い)ここでは、画像の説明を入力します。今までは intersect_safe最良の結果が得られました。あなたと最悪のアンダースコア。
neoswf 2012

にa breakを追加すると、操作Simple js loops数/秒が1,000万回に増加します
Richard

19
場合は、あなたはそれを逃し:最も簡単な答えは受け入れられたものではありませんが、下の1:stackoverflow.com/questions/1885557/...は
redben

回答:


1080

組み合わせを使用Array.prototype.filterしてArray.prototype.indexOf

array1.filter(value => -1 !== array2.indexOf(value))

またはvrugtehagelがコメントで示唆しているように、Array.prototype.includesより単純なコードには最新のものを使用できます。

array1.filter(value => array2.includes(value))

古いブラウザの場合:

array1.filter(function(n) {
    return array2.indexOf(n) !== -1;
});

9
アレイのプロトタイプにライブラリバージョンを追加することで解決できます。
アノン。

12
はい、しかし言及する価値がありました。
Tim Down

18
ここでは、簡単にするためと、非数値を扱うための最良の回答
Muhd

41
intersection([1,2,1,1,3], [1])を返します[1, 1, 1]。それはちょうど戻るべきではありません[1]か?
edjroot 2016年

21
代わりに、さらに単純なコードをarray2.indexOf(n) != -1書くこともできarray2.includes(n)ます。
vrugtehagel

157

特に入力がソートされていると想定できる場合、破壊的な方法が最も簡単に見えます。

/* destructively finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *  State of input arrays is undefined when
 *  the function returns.  They should be 
 *  (prolly) be dumped.
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length, b.length)
 */
function intersection_destructive(a, b)
{
  var result = [];
  while( a.length > 0 && b.length > 0 )
  {  
     if      (a[0] < b[0] ){ a.shift(); }
     else if (a[0] > b[0] ){ b.shift(); }
     else /* they're equal */
     {
       result.push(a.shift());
       b.shift();
     }
  }

  return result;
}

インデックスを追跡する必要があるため、非破壊的なものはより複雑な髪でなければなりません。

/* finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length(), b.length())
 */
function intersect_safe(a, b)
{
  var ai=0, bi=0;
  var result = [];

  while( ai < a.length && bi < b.length )
  {
     if      (a[ai] < b[bi] ){ ai++; }
     else if (a[ai] > b[bi] ){ bi++; }
     else /* they're equal */
     {
       result.push(a[ai]);
       ai++;
       bi++;
     }
  }

  return result;
}

14
には多数のエラーがありますintersect_safelengthはメソッドではなく配列のプロパティです。undelared変数ありますiではresult.push(a[i]);。最後に、これは単に一般的なケースでは機能しません>。演算子によれば、どちらも他方より大きくない2つのオブジェクトは必ずしも等しくありません。intersect_safe( [ {} ], [ {} ] )たとえば、(前述のエラーが修正されると)要素が1つの配列が表示されますが、これは明らかに間違っています。
Tim Down

1
@ティムダウン:あなたが指摘した構文エラーを修正しました。ゲータラーも以下も同じであると見なすことが正しいか正しくないかは、要件によって異なります。元の質問では、コンテンツに配列が含まれることが予想されるということには気づきませんでした。さて、予期しない入力を処理する必要があると言うのは当然ですが、仕様で(偶然にも)入力が数値の配列でなければならないことが既に規定されている場合、コードは問題ありません。
2009

1
@atk:問題の例として、数値のみを含む配列を使用しているので、私はあなたの要点を理解します。
Tim Down

4
を使用.slice(0)して、intersect_safeインデックスを追跡するのではなく、で配列のクローンを作成することもできます。
johnluetke、2011

1
@thesmart:あなたの言う通り、それを行うにはより確実な方法があります。上記のコードは、高速ではなくシンプルになるように設計されました:)
atk

59

お使いの環境がECMAScript 6 Setをサポートしている場合、1つのシンプルで効率的な(仕様リンクを参照)方法:

function intersect(a, b) {
  var setA = new Set(a);
  var setB = new Set(b);
  var intersection = new Set([...setA].filter(x => setB.has(x)));
  return Array.from(intersection);
}

短くなりますが読みにくくなります(追加の交差を作成しない場合も同様Set)。

function intersect(a, b) {
      return [...new Set(a)].filter(x => new Set(b).has(x));
}

毎回新しいSetものを避けるb

function intersect(a, b) {
      var setB = new Set(b);
      return [...new Set(a)].filter(x => setB.has(x));
}

セットを使用する場合、異なる値のみを取得するため、にnew Set[1,2,3,3].size評価されることに注意してください3


1
この[...setA]構文は何ですか?特別な種類のJavaScript操作?
jxramos 2017

1
@jxramosはSpread構文であり、この場合はセットの要素から配列を作成するために使用されています。「Array.from(setA)」もこの場合は機能しますが、質問で「最も簡単」を要求するため、その行を読みやすくするようにしました。それに関しては、コードがもっと簡単になると思うので、スニペットを更新します。
nbarbosa 2017

@nbarbosa気になる:なぜ配列aを「クローン」したのですか?#filterは元の配列を破壊しませんよね?新しい配列を作成しますか?
bplittle 2017年

@bplittle重複を削除するためにセットから配列を作成しました。それ以外の場合、配列を直接使用すると重複が返されます。たとえば、配列を直接使用した場合、intersect([1,2,2,4]、[2,3])は[2、2]になります。
nbarbosa 2017

2
そのx => new Set(b).has(x)矢印関数は、実行されるたびにbセットになりませんか?そのセットを変数に保存する必要があります。
2018

39

Underscore.jsまたはlodash.jsの使用

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]

20
Opは「ライブラリフリー」を要求しました。
LinuxDisciple 2016年

@LinuxDiscipleそれを逃したことについての私の間違い。おかげでメモ
Sai Ram

33
いずれにしても、これはこの検索の上位のGoogleリストであるため、ライブラリの回答があると便利です。ありがとう。
webnoob 2017年

私もこれが投稿されてうれしいです。初めてunderscore.jsの必要性を感じました。通常、JavaScriptのmapとreduceパイプラインはエレガントに機能しますが、今回はそうではありません。
Sridhar Sarnobat

アンダースコアは3未満、ジェレミーアシュケナス(作成者)は3未満ですが、それでも、代わりにLodashをチェックすることを強くお勧めします。これは基本的に、Underscoreの優れたバージョン(元々はフォークでした)で、ソースコードが非常に最適化されている(したがって、ほとんど読み取れません)だけです。Underscoreの人々は、Underscoreを完全に削除することを検討しました(そして、Lodashを使用するように人々に伝えているだけです)が、ソースコードの読みやすさを気にする人々は、それを維持することを主張しました(私は実際にはその側にいましたが、その後Lodashに変換しました)。@see github.com/jashkenas/underscore/issues/2182
machineghost

14

ES6用語での私の貢献。一般に、引数として指定された配列の数が不定である配列の共通部分を検出します。

Array.prototype.intersect = function(...a) {
  return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
}
var arrs = [[0,2,4,6,8],[4,5,6,7],[4,6]],
     arr = [0,1,2,3,4,5,6,7,8,9];

document.write("<pre>" + JSON.stringify(arr.intersect(...arrs)) + "</pre>");


このコードは見栄えは良いですが、完全には理解していません。説明して頂けますか?
通常の2018

1
@novemberskyこれは、すべてのサブジェクト配列を配列のように収集して[[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]適用します.reduce()。最初の[0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)操作が実行され、結果は新しいものpcなり[4,5,6,7]、次のターンになり、c残りがなくなるまで続きます。
Redu

1
大きなデータセットを扱う場合、これは高価なソリューションです。
Madbreaks、2018年

1
prototype不必要に変更しないでください。
fregante

14

// Return elements of array a that are also in b in linear time:
function intersect(a, b) {
  return a.filter(Set.prototype.has, new Set(b));
}

// Example:
console.log(intersect([1,2,3], [2,3,4,5]));

大規模な入力での他の実装よりも優れた簡潔なソリューションをお勧めします。小さな入力でのパフォーマンスが重要な場合は、以下の選択肢を確認してください。

代替案とパフォーマンスの比較:

代替実装については次のスニペットを参照し、パフォーマンスの比較についてはhttps://jsperf.com/array-intersection-comparisonを確認してください

Firefox 53での結果:

  • 大規模アレイ(10,000要素)での操作数/秒:

    filter + has (this)               523 (this answer)
    for + has                         482
    for-loop + in                     279
    filter + in                       242
    for-loops                          24
    filter + includes                  14
    filter + indexOf                   10
  • 小さな配列(100要素)での操作数/秒:

    for-loop + in                 384,426
    filter + in                   192,066
    for-loops                     159,137
    filter + includes             104,068
    filter + indexOf               71,598
    filter + has (this)            43,531 (this answer)
    filter + has (arrow function)  35,588

2
intersect([1,2,2,3], [2,3,4,5])を返します[2, 2, 3]
SeregPie

1
@SeregPieそのとおりです。コメントどおり「bにもある配列aの要素を返す」
le_m

質の高い回答ですが、opの質問は配列の交差についてのみ尋ねられ、重複の処理方法について言及/規定していないため、Setsを使用すると結果が根本的に変わります。その恥ずかしがり屋ですが、重複が存在する場合、この回答は予期しない結果をもたらす可能性があります。
Madbreaks、2018年

1
大好きですが、「フィルター+インクルード」を使用して不要な関数を追加しました。試してみてくださいa.filter(b.includes)。これはかなり高速に実行されるはずです(関数のアップグレードと同じです)。
SEoF 2019

11

連想配列を使用するのはどうですか?

function intersect(a, b) {
    var d1 = {};
    var d2 = {};
    var results = [];
    for (var i = 0; i < a.length; i++) {
        d1[a[i]] = true;
    }
    for (var j = 0; j < b.length; j++) {
        d2[b[j]] = true;
    }
    for (var k in d1) {
        if (d2[k]) 
            results.push(k);
    }
    return results;
}

編集:

// new version
function intersect(a, b) {
    var d = {};
    var results = [];
    for (var i = 0; i < b.length; i++) {
        d[b[i]] = true;
    }
    for (var j = 0; j < a.length; j++) {
        if (d[a[j]]) 
            results.push(a[j]);
    }
    return results;
}

1
これは、配列に文字列または数値しか含まれておらず、ページ内のどのスクリプトもに干渉していない場合にのみ有効Object.prototypeです。
Tim Down

2
OPの例では数値を使用していました。スクリプトがObject.prototypeに干渉した場合は、スクリプトを書き直すか削除する必要があります。
Steven Huwig、2009

(d1)と(d2)の両方は必要ありません。(d2)を作成し、(d1)をループする代わりに(a)をループします。
StanleyH

(ではない)のd[b[i]] = true;代わりにすべきです。ただし、編集には6文字が必要です。d[b[j]] = true;ij
Izhaki

@Izhakiありがとう、修正されました。(最小編集要件を回避するために//コメントを追加しました。)
Steven Huwig

8
  1. 並べ替え
  2. インデックス0から1つずつチェックし、そこから新しい配列を作成します。

このようなものですが、十分にテストされていません。

function intersection(x,y){
 x.sort();y.sort();
 var i=j=0;ret=[];
 while(i<x.length && j<y.length){
  if(x[i]<y[j])i++;
  else if(y[j]<x[i])j++;
  else {
   ret.push(x[i]);
   i++,j++;
  }
 }
 return ret;
}

alert(intersection([1,2,3], [2,3,4,5]));

PS:アルゴリズムは数値と通常の文字列のみを対象としており、任意のオブジェクト配列の交差は機能しない可能性があります。


3
並べ替えは必ずしも任意のオブジェクトの配列に役立つとは限りません
Tim Down

配列がソートされていない場合、長さ1000の配列x長さ1000の配列を交差するときに約1,000,000回ループする必要があります
YOU

JavaScriptの任意のオブジェクトには自然なソート順がない、つまり、任意のオブジェクトの配列をソートしても、等しいオブジェクトがグループ化されないということは、あなたが私のポイントを逃したと思います。機能しない効率的なアルゴリズムを使用するのはよくありません。
Tim Down

申し訳ありませんが、私は「任意のオブジェクト」を逃しました、はい、あなたは正しいです。それらのオブジェクトはそれをソートすることができず、アルゴリズムはそれらで動作しない可能性があります。
YOU

8

プリミティブのソートされた配列に対する@atkの実装のパフォーマンスは、.shiftではなく.popを使用することで改善できます。

function intersect(array1, array2) {
   var result = [];
   // Don't destroy the original arrays
   var a = array1.slice(0);
   var b = array2.slice(0);
   var aLast = a.length - 1;
   var bLast = b.length - 1;
   while (aLast >= 0 && bLast >= 0) {
      if (a[aLast] > b[bLast] ) {
         a.pop();
         aLast--;
      } else if (a[aLast] < b[bLast] ){
         b.pop();
         bLast--;
      } else /* they're equal */ {
         result.push(a.pop());
         b.pop();
         aLast--;
         bLast--;
      }
   }
   return result;
}

jsPerf:http ://bit.ly/P9FrZKを使用してベンチマークを作成しました。.popを使用する方が約3倍高速です。


1
他の人への補足として-これは文字列ではなく数値に対してのみ機能します。
Izhaki 2012

あなたが交換した場合ことを注意a[aLast] > b[bLast]してa[aLast].localeCompare(b[bLast]) > 0(とと同じelse if以下)、これは文字列で動作します。
アンドリュー2013

1
ので、速度差は、アレイのサイズに依存する.popO(1)であり、.shift()O(N)である
Esailija

8

jQueryの使用:

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);

8
これはと書くこともできますがc = $(b).filter(a);、ドキュメントでは要素に対してのみ機能することが記載されているため、この種の配列操作をjQueryに依存することはお勧めしません。
Stryner、2015

1
これはopの質問に答えません:「最も簡単な、ライブラリフリーのコードは何ですか...」
Madbreaks

7

文字列または数値のみを含む配列の場合、他の回答のいくつかのように、並べ替えを行うことができます。任意のオブジェクトの配列の一般的なケースでは、長い道のりを避けることはできないと思います。以下は、パラメータとして提供される任意の数の配列の共通部分を提供しますarrayIntersection

var arrayContains = Array.prototype.indexOf ?
    function(arr, val) {
        return arr.indexOf(val) > -1;
    } :
    function(arr, val) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === val) {
                return true;
            }
        }
        return false;
    };

function arrayIntersection() {
    var val, arrayCount, firstArray, i, j, intersection = [], missing;
    var arrays = Array.prototype.slice.call(arguments); // Convert arguments into a real array

    // Search for common values
    firstArray = arrays.pop();
    if (firstArray) {
        j = firstArray.length;
        arrayCount = arrays.length;
        while (j--) {
            val = firstArray[j];
            missing = false;

            // Check val is present in each remaining array 
            i = arrayCount;
            while (!missing && i--) {
                if ( !arrayContains(arrays[i], val) ) {
                    missing = true;
                }
            }
            if (!missing) {
                intersection.push(val);
            }
        }
    }
    return intersection;
}

arrayIntersection( [1, 2, 3, "a"], [1, "a", 2], ["a", 1] ); // Gives [1, "a"]; 

これは、オブジェクトIDが唯一の等価形式である場合にのみ機能します。
Steven Huwig、2009

そうですね、でも、それはほとんどの人にとって自然なことだと思います。また、別の等価テストを実行するために代替関数を接続するのは簡単です。
Tim Down

あなたの例では、誤ってグローバル変数firstArrを作成していると思います。
ジェイソンジャクソン

@JasonJackson:そうです、ありがとう。変数を呼び出すかどうか、firstArrまたはfirstArrayすべての参照を更新しないかどうかについて、私の考えが明らかに変わりました。修繕。
Tim Down

7

ES2015とセットを使用すると、かなり短くなります。文字列のような配列のような値を受け入れ、重複を削除します。

let intersection = function(a, b) {
  a = new Set(a), b = new Set(b);
  return [...a].filter(v => b.has(v));
};

console.log(intersection([1,2,1,2,3], [2,3,5,4,5,3]));

console.log(intersection('ccaabbab', 'addb').join(''));


[... a]を使用してセットから配列に変換すると、重複したアイテムが削除されます。良いアイデアです。ありがとう
V-SHY

1
このソリューションは、以前に2回提供されました。
Madbreaks、2018年

7

Setas thisArgを使用し、コールバックとしてArray#filter取得できSet#hasます。

function intersection(a, b) {
    return a.filter(Set.prototype.has, new Set(b));
}

console.log(intersection([1, 2, 3], [2, 3, 4, 5]));


なぜこれ以上賛成票を得られなかったのか分かりません。それは明らかに最良の答えです。
Paul Rooney

5

ここで最小のもの(filter / indexOf solution)を少し調整すると、つまり、JavaScriptオブジェクトを使用して配列の1つに値のインデックスを作成すると、O(N * M)から「おそらく」線形時間に減少します。ソース1 ソース2

function intersect(a, b) {
  var aa = {};
  a.forEach(function(v) { aa[v]=1; });
  return b.filter(function(v) { return v in aa; });
}

これは非常に単純な解決策ではなく(filter + indexOfよりもコードが多い)、非常に高速でもありません(おそらく、intersect_safe()よりも定数係数の分遅い)が、かなり良いバランスのようです。これは非常に単純な側面でありながら、優れたパフォーマンスを提供し、事前にソートされた入力を必要としません。


5

一度に任意の数の配列を処理できる別のインデックス付きアプローチ:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = 0;
            index[v]++;
        };
    };
    var retv = [];
    for (var i in index) {
        if (index[i] == arrLength) retv.push(i);
    };
    return retv;
};

文字列として評価できる値に対してのみ機能し、次のような配列として渡す必要があります。

intersect ([arr1, arr2, arr3...]);

...しかし、オブジェクトをパラメーターとして、または交差する要素のいずれかとして透過的に受け入れます(常に共通の値の配列を返します)。例:

intersect ({foo: [1, 2, 3, 4], bar: {a: 2, j:4}}); // [2, 4]
intersect ([{x: "hello", y: "world"}, ["hello", "user"]]); // ["hello"]

編集:これは、ある意味で少しバグがあることに気づきました。

つまり、入力配列自体に繰り返しを含めることはできないと考えてコーディングしました(例に含まれていない場合)。

ただし、入力配列に繰り返しが含まれていると、誤った結果が生成されます。例(以下の実装を使用):

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]);
// Expected: [ '1' ]
// Actual: [ '1', '3' ]

幸い、これは、第2レベルのインデックスを追加するだけで簡単に修正できます。あれは:

変化する:

        if (index[v] === undefined) index[v] = 0;
        index[v]++;

沿って:

        if (index[v] === undefined) index[v] = {};
        index[v][i] = true; // Mark as present in i input.

...そして:

         if (index[i] == arrLength) retv.push(i);

沿って:

         if (Object.keys(index[i]).length == arrLength) retv.push(i);

完全な例:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = {};
            index[v][i] = true; // Mark as present in i input.
        };
    };
    var retv = [];
    for (var i in index) {
        if (Object.keys(index[i]).length == arrLength) retv.push(i);
    };
    return retv;
};

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]); // [ '1' ]

2
これは、わずかな変更を加えた場合の最良の回答です。var v = 行の後に追加するif (typeof v == 'function') continue;と、結果への関数の追加がスキップされます。ありがとう!
Zsolti

@Zsoltiに感謝します。関数(およびそれを処理する方法)を持つことは元の質問の範囲外であるため、提案を追加しません。しかし、私の編集を参照してください。入力配列に繰り返しがある可能性がある場合、元の実装にはバグがあります。編集で修正しました。...一方、繰り返しがないことが確実にわかっている場合は、元の実装の方が少し安価です。
bitifet

...関数については、それらも交差する可能性があります。@ Zsoltiが言うようにそれらを検出した場合(をif (typeof v == 'function')使用すると、その文字列化(v.toString())をインデックスのキーとして使用できます。しかし、それをそのまま保持するために何かを行う必要があります。そうするための最も簡単な方法は、そのような場合には、最新のdeindexatonもこの状態を検出し、正しい値(機能)を復元するように変更する必要があり、代わりにシンプルなブール値trueの値として本来の機能を割り当てるだけである。しかし。。
bitifet

これは、100個の要素を持つ30個の配列でどれくらい速くできますか?CPUの使用率はどうですか
1

5

使用できます(IEを除くすべてのブラウザー)。

const intersection = array1.filter(element => array2.includes(element));

またはIEの場合:

const intersection = array1.filter(element => array2.indexOf(element) !== -1);

それを関数に変換できたらいいのですが
avalanche1

@ avalanche1 const交差=(a1、a2)=> a1.filter(e => a2.includes(e));
jota3

4
function intersection(A,B){
var result = new Array();
for (i=0; i<A.length; i++) {
    for (j=0; j<B.length; j++) {
        if (A[i] == B[j] && $.inArray(A[i],result) == -1) {
            result.push(A[i]);
        }
    }
}
return result;
}

4

データにいくつかの制限があれば、線形でそれを行うことができます時間で!

以下のための正の整数:ブール「見て/見たことがない」に値をマッピングする配列を使用します。

function intersectIntegers(array1,array2) { 
   var seen=[],
       result=[];
   for (var i = 0; i < array1.length; i++) {
     seen[array1[i]] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if ( seen[array2[i]])
        result.push(array2[i]);
   }
   return result;
}

オブジェクトにも同様の手法があります。ダミーキーを取得し、array1の各要素に対して「true」に設定してから、array2の要素でこのキーを探します。完了したらクリーンアップします。

function intersectObjects(array1,array2) { 
   var result=[];
   var key="tmpKey_intersect"
   for (var i = 0; i < array1.length; i++) {
     array1[i][key] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if (array2[i][key])
        result.push(array2[i]);
   }
   for (var i = 0; i < array1.length; i++) {
     delete array1[i][key];
   }
   return result;
}

もちろん、前にキーが表示されていないことを確認する必要があります。そうしないと、データが破壊されます...


ちなみに、これは任意の数の配列と交差するように簡単に拡張できます。ブール値を整数に置き換え、表示されるたびにインクリメントします。最後のラウンドの交差点を簡単に読み取ることができます。
タルーレン、2015年

興味深い解決策、私はそれが好きです。他のほとんどの解はO(n ^ 2)ですが、これはO(n)です。私はここericPのパフォーマンスフィドルに整数コードを追加jsfiddle.net/321juyLu/2。それは3番目に来た、私と同じように:)
rmcsharry

3

私にとって最も効果のあるものを提供します。

if (!Array.prototype.intersect){
Array.prototype.intersect = function (arr1) {

    var r = [], o = {}, l = this.length, i, v;
    for (i = 0; i < l; i++) {
        o[this[i]] = true;
    }
    l = arr1.length;
    for (i = 0; i < l; i++) {
        v = arr1[i];
        if (v in o) {
            r.push(v);
        }
    }
    return r;
};
}


2

'use strict'

// Example 1
function intersection(a1, a2) {
    return a1.filter(x => a2.indexOf(x) > -1)
}

// Example 2 (prototype function)
Array.prototype.intersection = function(arr) {
    return this.filter(x => arr.indexOf(x) > -1)
} 

const a1 = [1, 2, 3]
const a2 = [2, 3, 4, 5]

console.log(intersection(a1, a2))
console.log(a1.intersection(a2))


2

ES2015での機能的アプローチ

関数型のアプローチでは、副作用のない純粋な関数のみを使用することを検討する必要があります。それぞれの副作用は単一のジョブにのみ関係しています。

これらの制限により、関連する機能の構成可能性と再利用性が強化されます。

// small, reusable auxiliary functions

const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const apply = f => x => f(x);


// intersection

const intersect = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? true
     : false
  ) (xs);
};


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run it

console.log( intersect(xs) (ys) );

ネイティブ Setルックアップパフォーマンスが優れタイプが使用され。

重複を避ける

最初から繰り返し発生するアイテムは明らかにArray保持され、2番目のアイテムArrayは重複排除されます。これは望ましい動作である場合とそうでない場合があります。一意の結果が必要な場合dedupeは、最初の引数に適用します。

// auxiliary functions

const apply = f => x => f(x);
const comp = f => g => x => f(g(x));
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// intersection

const intersect = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? true
     : false
  ) (xs);
};


// de-duplication

const dedupe = comp(afrom) (createSet);


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// unique result

console.log( intersect(dedupe(xs)) (ys) );

任意の数の交差を計算する Array sの

任意の数のArraysの共通部分を計算する場合は、単にで構成intersectfoldlます。これは便利な関数です:

// auxiliary functions

const apply = f => x => f(x);
const uncurry = f => (x, y) => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);


// intersection

const intersect = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? true
     : false
  ) (xs);
};


// intersection of an arbitrarily number of Arrays

const intersectn = (head, ...tail) => foldl(intersect) (head) (tail);


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];
const zs = [0,1,2,3,4,5,6];


// run

console.log( intersectn(xs, ys, zs) );


印象的な機能:Haskellではないことを確認するために、ダブルテイクを行う必要がありました。唯一のヒントは:(expr ? true : false)冗長です。ちょうど使用しexpr、実際の真偽値を必要としない場合は、ちょうどtruthy / falsy。
jose_castro_arnaud 2017年

2

簡単にするために:

// Usage
const intersection = allLists
  .reduce(intersect, allValues)
  .reduce(removeDuplicates, []);


// Implementation
const intersect = (intersection, list) =>
  intersection.filter(item =>
    list.some(x => x === item));

const removeDuplicates = (uniques, item) =>
  uniques.includes(item) ? uniques : uniques.concat(item);


// Example Data
const somePeople = [bob, doug, jill];
const otherPeople = [sarah, bob, jill];
const morePeople = [jack, jill];

const allPeople = [...somePeople, ...otherPeople, ...morePeople];
const allGroups = [somePeople, otherPeople, morePeople];

// Example Usage
const intersection = allGroups
  .reduce(intersect, allPeople)
  .reduce(removeDuplicates, []);

intersection; // [jill]

利点:

  • 単純な汚れ
  • データ中心
  • 任意の数のリストで機能します
  • リストの任意の長さで機能します
  • 任意のタイプの値で機能します
  • 任意のソート順で機能します
  • 形状を保持します(任意の配列の最初の出現順)
  • 可能な限り早期に終了する
  • 関数/配列のプロトタイプを改ざんすることなく、メモリセーフ

欠点:

  • より高いメモリ使用量
  • CPU使用率が高い
  • 削減の理解が必要
  • データフローの理解が必要

これを3Dエンジンやカーネルの作業に使用するのは望ましくありませんが、イベントベースのアプリでこれを実行するのに問題がある場合は、デザインに大きな問題があります。


2

.reduceマップを作成し.filter、交差点を見つけます。delete内では.filter、2番目の配列を一意のセットであるかのように扱うことができます。

function intersection (a, b) {
  var seen = a.reduce(function (h, k) {
    h[k] = true;
    return h;
  }, {});

  return b.filter(function (k) {
    var exists = seen[k];
    delete seen[k];
    return exists;
  });
}

このアプローチはかなり簡単に推論できます。それは一定の時間で実行されます。


2

これは、list1.filter(n => list2.includes(n))を除いて、おそらく最も単純なものです。

var list1 = ['bread', 'ice cream', 'cereals', 'strawberry', 'chocolate']
var list2 = ['bread', 'cherry', 'ice cream', 'oats']

function check_common(list1, list2){
	
	list3 = []
	for (let i=0; i<list1.length; i++){
		
		for (let j=0; j<list2.length; j++){	
			if (list1[i] === list2[j]){
				list3.push(list1[i]);				
			}		
		}
		
	}
	return list3
	
}

check_common(list1, list2) // ["bread", "ice cream"]


これはO(nm)時間の複雑さを持っています...これはO(n + m)で解決できます
alchuang

2

交差する複数の配列を処理する必要がある場合:

const intersect = (a, b, ...rest) => {
  if (rest.length === 0) return [...new Set(a)].filter(x => new Set(b).has(x));
  return intersect(a, intersect(b, ...rest));
};

console.log(intersect([1,2,3,4,5], [1,2], [1, 2, 3,4,5], [2, 10, 1])) // [1,2]


しかし、100個の要素を持つ30個のアレイに対するこのソリューションはどのくらい高速ですか?
1

これは、JavaScriptメソッドのネイティブのみを使用しているため、コードが実行されるVMは、可能な限り自由に最適化できます。このコメントの時代に比べて、V8の最新バージョンでこれを実行していることを考えると、これより速い解決策は存在しないことは非常に肯定的です。
Belfordz

2

ES6スタイルのシンプルな方法。

const intersection = (a, b) => {
  const s = new Set(b);
  return a.filter(x => s.has(x));
};

例:

intersection([1, 2, 3], [4, 3, 2]); // [2, 3]

2

これらのオブジェクトの特定のプロパティに基づいて、オブジェクトの配列の交差を検出することもできるintesection関数を作成しました。

例えば、

if arr1 = [{id: 10}, {id: 20}]
and arr2 =  [{id: 20}, {id: 25}]

idプロパティに基づいて交差が必要な場合、出力は次のようになります。

[{id: 20}]

そのため、同じ関数(注:ES6コード)は次のとおりです。

const intersect = (arr1, arr2, accessors = [v => v, v => v]) => {
    const [fn1, fn2] = accessors;
    const set = new Set(arr2.map(v => fn2(v)));
    return arr1.filter(value => set.has(fn1(value)));
};

次のように関数を呼び出すことができます。

intersect(arr1, arr2, [elem => elem.id, elem => elem.id])

また、この関数は、最初の配列がプライマリ配列であることを考慮して交差を検出するため、交差の結果はプライマリ配列の結果になります。


2

map.has()が〜O(1)であると仮定すると、これはO(array1 + array2)時間で速くなると思います。間違っている場合は修正してください。

const intersection = (a1, a2) => {
  let map = new Map();
  let result = []
  for (let i of a1) {
    if (!map.has(i)) map.set(i, true);
  }
  for (let i of a2) {
    if (map.has(i)) result.push(i)
  }
  return result;
}


1

これがunderscore.jsの実装です。

_.intersection = function(array) {
  if (array == null) return [];
  var result = [];
  var argsLength = arguments.length;
  for (var i = 0, length = array.length; i < length; i++) {
    var item = array[i];
    if (_.contains(result, item)) continue;
    for (var j = 1; j < argsLength; j++) {
      if (!_.contains(arguments[j], item)) break;
    }
    if (j === argsLength) result.push(item);
  }
  return result;
};

出典:http : //underscorejs.org/docs/underscore.html#section-62


undesrcoreが利用可能であれば、悪いリファレンスではありません
Dimitrios Mistriotis
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.