Kode paling sederhana untuk persimpangan larik dalam javascript

611

Apa kode sederhana, bebas perpustakaan untuk mengimplementasikan persimpangan array di javascript? saya ingin menulis

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

dan dapatkan

[2, 3]
Peter
sumber
16
Apakah Anda ingin yang sederhana atau cepat?
SLaks
11
Prioritas sederhana, tetapi tidak bisa begitu mati otak bahwa itu akan menjadi babi kinerja :)
Peter
4
Saya telah membuat halaman uji JsFiddle Banchmark untuk semua metode di sini, termasukfungsi persimpangan _underscore . (lebih tinggi lebih baik) ! masukkan deskripsi gambar di sini Sampai sekarang intersect_safe memberikan hasil terbaik . ANDA & Tekankan yang terburuk.
neoswf
Menambahkan breakuntuk Simple js loopsmeningkatkan ops / detik hingga ~ 10M
Richard
19
Jika Anda melewatkannya : jawaban paling sederhana bukanlah jawaban yang diterima tetapi jawaban di bawah: stackoverflow.com/questions/1885557/…
redben

Jawaban:

1081

Gunakan kombinasi dari Array.prototype.filterdan Array.prototype.indexOf:

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

Atau seperti yang disarankan vrugtehagel di komentar, Anda dapat menggunakan yang lebih baru Array.prototype.includesuntuk kode yang lebih sederhana:

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

Untuk browser lama:

array1.filter(function(n) {
    return array2.indexOf(n) !== -1;
});
Segera.
sumber
9
Yang bisa Anda pecahkan dengan menambahkan versi pustaka pada prototipe array.
Anon.
12
Ya, tapi itu layak disebut.
Tim Down
18
Jawaban terbaik di sini, baik untuk kesederhanaan dan bekerja dengan non-angka
Muhd
41
intersection([1,2,1,1,3], [1])kembali [1, 1, 1]. Bukankah seharusnya kembali [1]?
edjroot
21
Alih-alih array2.indexOf(n) != -1satu juga dapat menulis array2.includes(n)untuk kode lebih sederhana.
vrugtehagel
157

Merusak tampaknya paling sederhana, terutama jika kita dapat berasumsi bahwa input diurutkan:

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

Non-destruktif harus menjadi rambut yang lebih rumit, karena kita harus melacak indeks:

/* 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;
}
atk
sumber
14
Ada banyak kesalahan di intersect_safe: lengthadalah properti di Array, bukan metode. Ada variabel yang tidak dihapus idalam result.push(a[i]);. Akhirnya, ini sama sekali tidak berfungsi dalam kasus umum: dua objek di mana tidak ada yang lebih besar dari yang lain menurut >operator belum tentu sama. intersect_safe( [ {} ], [ {} ] ), misalnya, akan memberikan (setelah kesalahan yang disebutkan sebelumnya diperbaiki) array dengan satu elemen, yang jelas salah.
Tim Down
1
@Tim Down: Memperbaiki kesalahan sintaksis yang Anda tunjukkan. Apakah benar atau salah untuk mempertimbangkan sesuatu yang tidak sama atau kurang tergantung pada persyaratan. Saya tidak melihat apa pun dalam pertanyaan awal yang mengatakan bahwa konten diharapkan mengandung array. Sekarang, Anda akan benar untuk mengatakan bahwa input yang tidak terduga harus ditangani, tetapi jika spec sudah mendikte bahwa input harus berupa array angka (seperti yang saya duga) maka kode akan baik-baik saja.
atk
1
@ atk: Saya ambil poin Anda, melihat sebagai contoh dalam pertanyaan menggunakan array yang hanya berisi angka.
Tim Down
4
Anda juga dapat menggunakan .slice(0)untuk membuat klon dari array intersect_safe, bukan melacak indeks.
johnluetke
1
@thesmart: Anda benar, pasti ada lebih banyak cara untuk melakukannya. Kode, di atas, dimaksudkan untuk menjadi sederhana, tidak cepat :)
atk
59

Jika lingkungan Anda mendukung ECMAScript 6 Set , satu cara sederhana dan seharusnya efisien (lihat tautan spesifikasi):

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

Lebih pendek, tetapi kurang dapat dibaca (juga tanpa membuat persimpangan tambahan Set):

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

Menghindari yang baru Setdari bsetiap waktu:

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

Perhatikan bahwa saat menggunakan set Anda hanya akan mendapatkan nilai yang berbeda, jadi new Set[1,2,3,3].sizedievaluasi untuk 3.

nbarbosa
sumber
1
apa [...setA]sintaksis ini ? Beberapa jenis operasi javascript khusus?
jxramos
1
@ jxramos yaitu Menyebar sintaks dan dalam hal ini hanya digunakan untuk membuat array dari elemen-elemen di set. "Array.from (setA)" juga akan berfungsi dalam kasus ini, tetapi karena pertanyaannya meminta "paling sederhana" saya mencoba membuatnya lebih bersih untuk membaca di baris itu. Dalam hal ini, saya pikir kodenya bisa lebih sederhana, jadi saya akan memperbarui snippet.
nbarbosa
@nbarbosa Saya ingin tahu: mengapa Anda "mengkloning" array? #filter tidak merusak array asli, bukan? Itu menciptakan array baru?
bplittle
@ Bplittle Saya baru saja membuat array dari set untuk menghapus duplikat, jika tidak, menggunakan array secara langsung akan menghasilkan duplikat kembali. Sebagai contoh, jika saya menggunakan array secara langsung, berpotongan ([1,2,2,4], [2,3]) akan menghasilkan [2, 2].
nbarbosa
2
Bukankah x => new Set(b).has(x)fungsi panah itu berubah bmenjadi set setiap kali dieksekusi? Anda mungkin harus menyimpan set itu dalam sebuah variabel.
Aran-Fey
39

Menggunakan Underscore.js atau lodash.js

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]
Sai Ram
sumber
20
Op meminta "bebas perpustakaan".
LinuxDisciple
@LinuxDisciple Kesalahan saya karena melewatkan itu. Terima kasih atas catatan
Sai Ram
33
Bagaimanapun, ini adalah daftar google teratas untuk pencarian ini sehingga memiliki jawaban perpustakaan berguna. Terima kasih.
webnoob
Saya juga senang ini diposting. Pertama kali saya merasakan perlunya underscore.js. Biasanya JavaScript memetakan dan mengurangi saluran pipa melakukan pekerjaan dengan elegan tetapi tidak kali ini.
Sridhar Sarnobat
Saya <3 Menggarisbawahi dan saya <3 Jeremy Ashkenas (penciptanya), tetapi meskipun demikian saya SANGAT merekomendasikan memeriksa Lodash sebagai gantinya. Ini pada dasarnya adalah versi superior dari Underscore (awalnya garpu) yang satu-satunya kelemahannya adalah super-dioptimalkan (dan dengan demikian hampir tidak dapat dibaca) kode sumber. Orang-orang Underscore bahkan dianggap menyingkirkan Underscore sepenuhnya (dan hanya memberitahu orang-orang untuk menggunakan Lodash), tetapi orang-orang yang peduli dengan keterbacaan kode sumber berpendapat untuk mempertahankannya (saya sebenarnya ada di pihak itu, tetapi saya sejak itu telah dikonversi ke Lodash). @lihat github.com/jashkenas/underscore/issues/2182
machineghost
14

Kontribusi saya dalam istilah ES6. Secara umum ia menemukan persimpangan array dengan jumlah array yang tidak terbatas yang disediakan sebagai argumen.

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

Redu
sumber
kode ini terlihat hebat, tapi saya tidak mengerti sepenuhnya. Mungkin bisa menjelaskannya?
theusual
1
@novembersky Ia mengumpulkan semua array subjek dalam sebuah array seperti [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]dan kemudian berlaku .reduce(). [0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)Operasi pertama dilakukan dan hasilnya menjadi baru pdan cmenjadi [4,5,6,7]di belokan berikutnya dan berlanjut seterusnya hingga tidak ada lagi cyang tersisa.
Redu
1
Ini adalah solusi mahal jika Anda bekerja dengan kumpulan data besar.
Madbreak
1
Jangan memodifikasi yang prototypetidak perlu.
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]));

Saya merekomendasikan solusi ringkas di atas yang mengungguli implementasi lainnya pada input besar. Jika kinerja pada input kecil penting, periksa alternatif di bawah ini.

Alternatif dan perbandingan kinerja:

Lihat cuplikan berikut untuk implementasi alternatif dan periksa https://jsperf.com/array-intersection-comparison untuk perbandingan kinerja.

Hasil di Firefox 53:

  • Ops / dtk pada array besar (10.000 elemen):

    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
  • Ops / dtk pada array kecil (100 elemen):

    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
le_m
sumber
2
intersect([1,2,2,3], [2,3,4,5])kembali [2, 2, 3].
SeregPie
1
@SeregPie Persis. Sesuai komentar "Kembalikan elemen array a yang juga ada di b"
le_m
Jawaban kualitas, namun penggunaan Sets secara fundamental mengubah hasil karena pertanyaan op hanya menanyakan tentang persimpangan array dan tidak menyebutkan / menetapkan bagaimana menangani duplikat. Karena itu, jawaban ini dapat menghasilkan hasil yang tidak terduga ketika ada duplikat.
Madbreak
1
Senang, tetapi Anda telah menambahkan fungsi yang tidak perlu dengan "filter + include". coba a.filter(b.includes). Ini harus berjalan jauh lebih cepat (sama seperti peningkatan fungsi Anda).
SEoF
11

Bagaimana kalau hanya menggunakan array asosiatif?

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

edit:

// 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;
}
Steven Huwig
sumber
1
Ini hanya ada kemungkinan jika array Anda hanya berisi string atau angka, dan jika tidak ada skrip di halaman Anda yang berantakan Object.prototype.
Tim Down
2
Contoh OP menggunakan angka, dan jika skrip telah mengacaukan Object.prototype maka skrip harus ditulis ulang atau dihapus.
Steven Huwig
Anda tidak perlu keduanya (d1) dan (d2). Buat (d2), lalu lewati (a) alih-alih lewati (d1)
StanleyH
Seharusnya d[b[i]] = true;bukan d[b[j]] = true;( itidak j). Tetapi pengeditan membutuhkan 6 karakter.
Izhaki
@Izhaki terima kasih, sudah diperbaiki. (Menambahkan / / komentar untuk berkeliling persyaratan penyuntingan minimum.)
Steven Huwig
8
  1. Urutkan
  2. periksa satu per satu dari indeks 0, buat array baru dari itu.

Sesuatu seperti ini, Tidak diuji dengan baik.

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: Algoritma hanya ditujukan untuk Bilangan dan String Normal, persimpangan array objek arbiter mungkin tidak berfungsi.

KAMU
sumber
3
Penyortiran tidak akan selalu membantu untuk array objek yang sewenang-wenang
Tim Down
Jika array tidak diurutkan, perlu mengulang sekitar 1.000.000 kali ketika Anda memotong 1000 array panjang x 1000 array panjang
ANDA
Saya pikir Anda melewatkan maksud saya, yaitu bahwa objek arbitrase dalam JavaScript tidak memiliki urutan pengurutan alami, artinya mengurutkan array objek arbitrer tidak akan menghasilkan objek yang sama dikelompokkan. Tidak ada gunanya memiliki algoritma yang efisien yang tidak berfungsi.
Tim Down
Ah maaf, saya melewatkan "objek sewenang-wenang", ya, Anda benar. objek tersebut tidak dapat mengurutkannya, dan algoritme mungkin tidak berfungsi pada mereka.
ANDA
8

Kinerja implementasi @ atk untuk array primitif yang diurutkan dapat ditingkatkan dengan menggunakan .pop daripada .shift.

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

Saya membuat tolok ukur menggunakan jsPerf: http://bit.ly/P9FrZK . Sekitar tiga kali lebih cepat untuk menggunakan .pop.

xn.
sumber
1
Sama seperti catatan tambahan untuk orang lain - ini hanya akan berfungsi untuk angka, bukan string.
Izhaki
Perhatikan bahwa jika Anda mengganti a[aLast] > b[bLast]dengan a[aLast].localeCompare(b[bLast]) > 0(dan sama dengan di else ifbawah) maka ini akan berfungsi pada string.
andrew
1
Perbedaan kecepatan tergantung pada ukuran array karena .popO (1) dan .shift()O (n)
Esailija
8

Menggunakan jQuery :

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);
Gowsikan
sumber
8
Ini juga bisa ditulis c = $(b).filter(a);, tetapi saya tidak akan merekomendasikan bergantung pada jQuery untuk manipulasi array semacam ini karena dokumentasi hanya menyebutkan bahwa itu berfungsi untuk elemen.
Stryner
1
Ini tidak menjawab pertanyaan op: "Apa yang paling sederhana, kode bebas perpustakaan ..."
Madbreaks
7

Untuk array yang hanya berisi string atau angka, Anda dapat melakukan sesuatu dengan menyortir, seperti beberapa jawaban lainnya. Untuk kasus umum array benda sewenang-wenang, saya tidak berpikir Anda bisa menghindari melakukannya jauh-jauh. Berikut ini akan memberi Anda persimpangan sejumlah array yang disediakan sebagai parameter untuk 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"]; 
Tim Down
sumber
Ini hanya bekerja dalam kasus di mana identitas objek adalah satu-satunya bentuk kesetaraan.
Steven Huwig
Ya, tapi saya pikir itulah yang tampaknya alami bagi kebanyakan orang. Ini juga mudah untuk memasukkan fungsi alternatif untuk melakukan tes kesetaraan yang berbeda.
Tim Down
Saya pikir Anda tidak sengaja membuat variabel global firstArr dalam contoh Anda.
Jason Jackson
@JasonJackson: Anda benar, terima kasih. Jelas berubah pikiran tentang apakah akan memanggil variabel firstArratau firstArraytidak dan tidak memperbarui semua referensi. Tetap.
Tim Down
7

Cukup singkat menggunakan ES2015 dan Perangkat. Menerima nilai seperti Array seperti String dan menghapus duplikat.

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

SeregPie
sumber
Konversi dari Set ke array dengan [... a] akan menghapus item duplikat, ide bagus, terima kasih
V-SHY
1
Solusi ini diberikan dua kali sebelum Anda.
Madbreak
7

Anda bisa menggunakan Setsebagai thisArgdari Array#filterdan mengambil Set#hassebagai callback.

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

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

Nina Scholz
sumber
Saya tidak tahu mengapa ini tidak mendapat suara lebih banyak. Itu jelas jawaban terbaik.
Paul Rooney
5

Tweak kecil ke yang terkecil di sini (solusi filter / indexOf ), yaitu membuat indeks nilai di salah satu array menggunakan objek JavaScript, akan menguranginya dari O (N * M) menjadi "mungkin" waktu linear. sumber1 sumber2

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

Ini bukan solusi yang paling sederhana (ini lebih dari kode filter + indexOf ), juga bukan yang paling cepat (mungkin lebih lambat oleh faktor konstan daripada intersect_safe () ), tetapi sepertinya keseimbangan yang cukup bagus. Itu di sisi yang sangat sederhana, sambil memberikan kinerja yang baik, dan itu tidak memerlukan input pra-disortir.

David
sumber
5

Pendekatan lain yang diindeks dapat memproses sejumlah array sekaligus:

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

Ini hanya berfungsi untuk nilai yang dapat dievaluasi sebagai string dan Anda harus meneruskannya sebagai array seperti:

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

... tetapi secara transparan menerima objek sebagai parameter atau sebagai salah satu elemen yang akan berpotongan (selalu mengembalikan array nilai-nilai umum). Contoh:

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

EDIT: Saya hanya memperhatikan bahwa ini, sedikit banyak, bermasalah.

Yaitu: Saya mengkodekannya berpikir bahwa input array tidak dapat dengan sendirinya mengandung pengulangan (seperti contoh yang diberikan tidak).

Tetapi jika array input kebetulan mengandung pengulangan, itu akan menghasilkan hasil yang salah. Contoh (menggunakan implementasi di bawah):

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

Untungnya ini mudah diperbaiki dengan hanya menambahkan pengindeksan tingkat kedua. Itu adalah:

Perubahan:

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

oleh:

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

...dan:

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

oleh:

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

Contoh lengkap:

// 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' ]
bitifet
sumber
2
Ini adalah jawaban terbaik dengan sedikit modifikasi. Setelah var v = baris tambahkan if (typeof v == 'function') continue;dan itu akan melewatkan fungsi menambahkan ke hasil. Terima kasih!
Zsolti
Terima kasih @Zsolti. Saya tidak menambahkan saran Anda karena memiliki fungsi (dan cara kami ingin menanganinya) berada di luar ruang lingkup pertanyaan aslinya. Tetapi lihat hasil edit saya: Jika Anda dapat memiliki pengulangan dalam array input Anda, maka implementasi asli buggy. Saya memperbaikinya di edit saya. ... Di sisi lain, jika Anda tahu pasti tidak akan ada pengulangan, implementasi asli sedikit lebih murah.
bitifet
... tentang fungsi, mereka juga dapat berpotongan: Jika kita mendeteksinya seperti kata @Zsolti (dengan if (typeof v == 'function'), maka kita dapat menggunakan stringifikasi ( v.toString()) sebagai kunci untuk indeks. Tapi, kita perlu melakukan sesuatu untuk mempertahankannya tetap utuh. Cara termudah untuk melakukannya adalah hanya menetapkan fungsi asli sebagai nilai alih-alih nilai boolean yang sebenarnya, tetapi, dalam hal itu, deindexaton terbaru juga harus diubah untuk mendeteksi kondisi ini dan mengembalikan nilai yang tepat (fungsi).
bitifet
Seberapa cepat ini bisa dengan 30 array dengan 100 elemen .. bagaimana penggunaan CPU?
Aidonsnous
5

Anda dapat menggunakan (untuk semua browser kecuali IE):

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

atau untuk IE:

const intersection = array1.filter(element => array2.indexOf(element) !== -1);
jota3
sumber
Akan lebih baik jika Anda bisa mengubahnya menjadi fungsi
avalanche1
@ avalanche1 const intersection = (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;
}
Gabe
sumber
4

Dengan beberapa batasan pada data Anda, Anda dapat melakukannya dalam waktu linier !

Untuk bilangan bulat positif : gunakan array yang memetakan nilai ke boolean "terlihat / tidak terlihat".

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

Ada teknik serupa untuk objek : ambil kunci dummy, setel ke "true" untuk setiap elemen dalam array1, lalu cari kunci ini di elemen array2. Bersihkan saat Anda selesai.

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

Tentu saja Anda perlu memastikan bahwa kunci tersebut tidak muncul sebelumnya, jika tidak, Anda akan menghancurkan data Anda ...

tarulen
sumber
Omong-omong, ini dapat dengan mudah diperluas untuk memotong sejumlah array: ganti boolean dengan bilangan bulat, dan naikkan setiap kali terlihat: Anda dapat dengan mudah membaca persimpangan di babak terakhir.
tarulen
Solusi menarik, saya suka itu. Sebagian besar solusi lain adalah O (n ^ 2), tetapi ini adalah O (n). Saya menambahkan kode integer ke biola kinerja ericP di sini jsfiddle.net/321juyLu/2 . Itu datang ke-3, saya suka :)
rmcsharry
3

Saya akan berkontribusi dengan apa yang paling berhasil bagi saya:

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;
};
}
Johan
sumber
3

"indexOf" untuk IE 9.0, chrome, firefox, opera,

    function intersection(a,b){
     var rs = [], x = a.length;
     while (x--) b.indexOf(a[x])!=-1 && rs.push(a[x]);
     return rs.sort();
    }

intersection([1,2,3], [2,3,4,5]);
//Result:  [2,3]
Martin Roberto Mondragon Sotel
sumber
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))

Vlad Bezden
sumber
2

Pendekatan fungsional dengan ES2015

Pendekatan fungsional harus mempertimbangkan hanya menggunakan fungsi murni tanpa efek samping, yang masing-masing hanya berkaitan dengan satu pekerjaan.

Pembatasan ini meningkatkan kemampuan menyusun dan penggunaan kembali fungsi yang terlibat.

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

Harap dicatat bahwa Setjenis asli digunakan, yang memiliki kinerja pencarian yang menguntungkan.

Hindari duplikat

Jelas item berulang kali terjadi dari yang pertama Arraydipertahankan, sedangkan yang kedua Arraydiduplikasi. Ini mungkin atau mungkin bukan perilaku yang diinginkan. Jika Anda membutuhkan hasil yang unik, hanya berlaku dedupeuntuk argumen pertama:

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

Hitung persimpangan dari sejumlah Arrays

Jika Anda ingin menghitung persimpangan jumlah sewenang-wenang Arrayhanya menulis intersectdengan foldl. Berikut ini adalah fungsi kenyamanan:

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


sumber
Fungsional yang mengesankan: harus melakukan pengambilan ganda untuk mengonfirmasi bahwa itu bukan Haskell. Satu-satunya nitpick adalah: (expr ? true : false)redundan. Gunakan hanya exprjika boolean yang sebenarnya tidak diperlukan, cukup jujur ​​/ palsu.
jose_castro_arnaud
2

Untuk kesederhanaan:

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

Manfaat:

  • kotoran sederhana
  • data-centric
  • bekerja untuk sejumlah daftar yang berubah-ubah
  • bekerja untuk daftar panjang sewenang-wenang
  • bekerja untuk jenis nilai yang berubah-ubah
  • bekerja untuk urutan sortir yang sewenang-wenang
  • mempertahankan bentuk (urutan penampilan pertama dalam array apa pun)
  • keluar lebih awal jika memungkinkan
  • memori aman, pendek gangguan dengan prototipe Function / Array

Kekurangan:

  • penggunaan memori yang lebih tinggi
  • penggunaan CPU lebih tinggi
  • membutuhkan pemahaman tentang pengurangan
  • membutuhkan pemahaman tentang aliran data

Anda tidak ingin menggunakan ini untuk mesin 3D atau kerja kernel, tetapi jika Anda memiliki masalah menjalankan ini di aplikasi berbasis acara, desain Anda memiliki masalah yang lebih besar.

Norguard
sumber
2

.reduceuntuk membangun peta, dan .filteruntuk menemukan persimpangan. deletedalam .filtermemungkinkan kita untuk memperlakukan array kedua seolah-olah itu satu set yang unik.

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

Saya menemukan pendekatan ini cukup mudah untuk dipikirkan. Ia melakukan dalam waktu yang konstan.

Belden
sumber
2

Ini mungkin yang paling sederhana, selain 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"]

Chris Lwin
sumber
ini memiliki kompleksitas waktu O (nm) ... ini dapat diselesaikan dalam O (n + m)
alchuang
2

Jika Anda perlu mengatasinya memotong beberapa array:

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]

Belfordz
sumber
Tetapi seberapa cepat solusi ini untuk 30 array dengan 100 elemen?
Aidonsnous
Ini hanya menggunakan metode Javascript asli, dan karenanya VM di mana kode akan berjalan bebas untuk mengoptimalkan sejauh mungkin. Saya cukup positif bahwa tidak ada solusi yang lebih cepat mengingat Anda menjalankan ini dalam versi modern V8 relatif terhadap usia komentar ini.
Belfordz
2

Cara sederhana ala ES6.

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

Contoh:

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

Saya telah menulis fungsi interseksi yang bahkan dapat mendeteksi persimpangan array objek berdasarkan properti tertentu dari objek-objek tersebut.

Contohnya,

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

dan kami ingin persimpangan berdasarkan idproperti, maka outputnya harus:

[{id: 20}]

Dengan demikian, fungsi untuk hal yang sama (catatan: kode ES6) adalah:

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

dan Anda dapat memanggil fungsinya sebagai:

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

Perhatikan juga: fungsi ini menemukan persimpangan mengingat array pertama adalah array primer dan dengan demikian hasil persimpangan akan menjadi array utama.

Mridul Meharia
sumber
2

Pikir ini akan lebih cepat dengan O (array1 + array2) waktu dengan asumsi map.has () adalah ~ O (1). Tolong koreksi saya jika salah.

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

ajaix
sumber
1

Berikut ini adalah implementasi 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;
};

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

Dorian
sumber
Bukan referensi buruk jika undesrcore tersedia
Dimitrios Mistriotis