Javascript'te dizi kesişimi için en basit kod


607

Javascript'te dizi kavşaklarını uygulamak için en basit, kütüphane içermeyen kod nedir? yazmak istiyorum

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

ve Al

[2, 3]

16
Basit mi hızlı mı?
SLaks

11
Öncelik basit, ama o kadar beyinsiz olamaz ki bir performans domuzu olacak :)
Peter

4
_Underscore kavşak işlevide dahil olmak üzere burada tüm yöntemler için bir JsFiddle Banchmark test sayfası yaptım . (daha yüksek daha iyidir) ! görüntü açıklamasını buraya girin şimdiye kadar intersect_safe en iyi sonuçları verdi . SİZ ve en kötüsü altını çizin.
neoswf

Bir ekleme breakiçin Simple js loops~ 10M artar ops / sn
Richard

Yanıtlar:


1078

Kombinasyonunu kullanın Array.prototype.filterve Array.prototype.indexOf:

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

Veya yorumlarda önerilen vrugtehagel'inArray.prototype.includes daha basit kod için daha yenisini kullanabilirsiniz :

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

Daha eski tarayıcılar için:

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

9
Dizinin prototipine bir kütüphane sürümü ekleyerek çözebilirsiniz.
Anon.

12
Evet, ama kayda değerdi.
Tim Down

18
Hem basitlik hem de sayı olmayanlarla çalışmak için en iyi cevap
Muhd

41
intersection([1,2,1,1,3], [1])döner [1, 1, 1]. Sadece geri dönmemeli [1]mi?
edjroot

21
Bir yerine daha basit kod için array2.indexOf(n) != -1de yazabilirsiniz array2.includes(n).
vrugtehagel

157

Yıkıcı en basit görünüyor, özellikle de girdinin sıralandığını varsayabilirsek:

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

Tahribatsız saçlar daha karmaşık olmalı, çünkü endeksleri takip etmeliyiz:

/* 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
Çok sayıda hata var intersect_safe: lengthDizilerde bir yöntem değil, bir özellik. 'De tanımlanmamış bir değişken ivar result.push(a[i]);. Son olarak, bu sadece genel durumda işe yaramaz: >operatöre göre ikisinden de daha büyük olmayan iki nesne mutlaka eşit değildir. intersect_safe( [ {} ], [ {} ] )örneğin, (daha önce belirtilen hatalar düzeltildikten sonra) bir öğeye sahip olan ve açıkça yanlış olan bir dizi verecektir.
Tim Down

1
@Tim Down: Belirttiğiniz sözdizimi hataları düzeltildi. Ne daha büyük ne de daha az eşit olan herhangi bir şeyi dikkate almanın doğru ya da yanlış olup olmadığı şartlara bağlıdır. Orijinal soruda, içeriğin diziler içermesi beklendiğini söyleyen hiçbir şey fark etmiyorum. Şimdi, beklenmedik girdinin işlenmesi gerektiğini söylemeye haklısınız, ancak eğer spesifikasyon zaten bu girdiyi dikte ettiyse (varsaydığım gibi) sayı dizileri olmalı, kod iyi olurdu.
atk

1
@atk: Demek istediğim, sorudaki örnekte yalnızca sayı içeren diziler kullanıldığını görmek.
Tim Down

4
.slice(0)Dizileri intersect_safeizlemek yerine dizinin bir kopyasını oluşturmak için de kullanabilirsiniz .
johnluetke

1
@thesmart: haklısın, bunu yapmanın kesinlikle daha etkili yolları var. Yukarıdaki kod, basit değil, hızlı değil :)
atk

58

Ortamınız ECMAScript 6 Set'i destekliyorsa , basit ve sözde verimli (spesifikasyon bağlantısına bakın) bir yol:

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

Daha kısa, ancak daha az okunabilir (ek kavşak oluşturmadan da Set):

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

Yeni kaçınmak Setgelen bher zaman:

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

Kümeleri kullanırken yalnızca farklı değerler elde edeceğinizi ve dolayısıyla new Set[1,2,3,3].sizedeğerlendirildiğinizi unutmayın 3.


1
bu [...setA]sözdizimi nedir? Özel bir tür javascript işlemi?
jxramos

1
Spread sözdizimi olan @jxramos ve bu durumda sadece kümedeki öğelerden bir dizi oluşturmak için kullanılır. "Array.from (setA)" da bu durumda işe yarayacaktır, ancak "en basit" için sorulan sorudan beri bu satırda okumayı daha temiz hale getirmeye çalıştım. Bu konuda, kodun daha basit olabileceğini düşünüyorum, bu yüzden snippet'i güncelleyeceğim.
nbarbosa

@nbarbosa Merak ediyorum: diziyi neden "klonladınız"? #filter orijinal diziyi yok etmez, değil mi? Yeni bir dizi mi oluşturur?
bplittle

@bplittle Yinelenenleri kaldırmak için kümeden bir dizi oluşturdum, aksi takdirde diziyi doğrudan kullanmak yinelenen sonuçların döndürülmesine neden olur. Örneğin, diziyi doğrudan kullandıysam, kesişme ([1,2,2,4], [2,3]) [2, 2] değerini verir.
nbarbosa

2
Bu x => new Set(b).has(x)ok işlevi her yürütüldüğünde bbir kümeye dönüşmüyor mu? Muhtemelen bu seti bir değişkene kaydetmelisiniz.
Aran-Fey

39

Underscore.js veya lodash.js kullanma

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

20
Op "kütüphane içermez" istedi.
Linux İlkesi

@LinuxDisciple Eksik olan hatam. Notlar için teşekkürler
Sai Ram

33
Her durumda, bu arama için en iyi google listesidir, bu nedenle kütüphane cevabına sahip olmak yararlıdır. Teşekkürler.
webnoob

Ben de bu yayınlanmıştır sevindim. İlk defa underscore.js'ye olan ihtiyacı hissettim. Genellikle JavaScript haritası ve boru hatlarını azaltma işi zarif yapar, ancak bu sefer değil.
Sridhar Sarnobat

Ben <3 Alt çizgi ve ben <3 Jeremy Ashkenas (yaratıcısı), ama buna rağmen Lodash'ı kontrol etmenizi şiddetle tavsiye ederim. Temel olarak, sadece dezavantajı süper optimize edilmiş (ve dolayısıyla neredeyse okunamaz) kaynak kodunun üstün bir alt çizgi versiyonudur (aslında bir çataldı). Alt çizgi insanları Alt çizgiden tamamen kurtulmayı bile düşündüler (ve sadece insanlara Lodash'ı kullanmalarını söylediler), ancak kaynak kodu okunabilirliğini önemseyen insanlar onu tutmayı savundu (aslında o taraftaydım, ancak o zamandan beri Lodash'a geçtim). @ github.com/jashkenas/underscore/issues/2182
machineghost

14

ES6 açısından katkım. Genelde, bağımsız değişken olarak sağlanan sınırsız sayıda dizinin bulunduğu bir dizinin kesişimini bulur.

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


Bu kod harika görünüyor, ama tamamen anlamıyorum. Açıklamak mümkün mü lütfen?
theusual

1
@novembersky Tüm konu dizilerini benzer bir dizide toplar [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]ve sonra uygular .reduce(). İlk [0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)operasyon gerçekleştirilir ve sonuç yeni hale gelir pve colur [4,5,6,7]sonraki sırayla ve artık kalmayıncaya kadar yukarı böylece devam cbırakılır.
Redu

1
Büyük veri kümeleriyle çalışıyorsanız bu pahalı bir çözümdür.
Madbreaks

1
prototypeGereksiz yere değişiklik yapmayın .
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]));

Yukarıda, büyük girdiler üzerindeki diğer uygulamalardan daha iyi performans gösteren kısa özümü öneririm. Küçük girişlerdeki performans önemliyse, aşağıdaki alternatifleri kontrol edin.

Alternatifler ve performans karşılaştırması:

Alternatif uygulamalar için aşağıdaki snippet'e bakın ve performans karşılaştırmaları için https://jsperf.com/array-intersection-comparison adresini kontrol edin .

Firefox 53 sonuçları:

  • Büyük dizilerde Ops / sn (10.000 eleman):

    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
  • Küçük dizilerde Ops / sn (100 eleman):

    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])döner [2, 2, 3].
SeregPie

1
@SeregPie Kesinlikle.
Yoruma göre

Bununla birlikte, kalite yanıtı, kümelerin kullanımı temel olarak sonuçları değiştirir, çünkü op'un sorusu sadece dizi kavşakları hakkında sorulur ve kopyaları nasıl ele alacağından bahsetmez. Bundan utanarak, bu cevap kopyalar olduğunda beklenmedik sonuçlar verebilir.
Madbreaks

1
Bayıldım, ancak "filter + include" ile gereksiz bir işlev eklediniz. deneyin a.filter(b.includes). Çok daha hızlı çalışmalıdır (işlev yükseltmenizle aynı).
SEoF

11

İlişkisel dizileri kullanmaya ne dersiniz?

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

Düzenle:

// 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
Bu, yalnızca dizilerinizde yalnızca dizeler veya sayılar varsa ve sayfanızdaki komut dosyasının hiçbiriyle uğraşmadığı takdirde bir şanstır Object.prototype.
Tim Down

2
OP örneği sayıları kullanıyordu ve bir komut dosyası Object.prototype ile uğraştıysa, komut dosyasının yeniden yazılması veya kaldırılması gerekir.
Steven Huwig

Hem (d1) hem de (d2) 'ye ihtiyacınız yok. (D2) öğesini oluşturun, ardından (d1) yerine döngü yapmak için (a) öğesini döndürün.
StanleyH

Olmalı d[b[i]] = true;yerine d[b[j]] = true;( ideğil j). Ancak düzenleme 6 karakter gerektirir.
Izhaki

@Izhaki teşekkürler, düzeltildi. (Minimum düzenleme gereksinimi hakkında bir // yorum eklendi.)
Steven Huwig

8
  1. Sırala
  2. 0 dizininden tek tek kontrol edin, bundan yeni dizi oluşturun.

Böyle bir şey, Ama iyi test değil.

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: Sadece Sayılar ve Normal Dizeler için tasarlanmış algoritma, arbitary nesne dizilerinin kesişimi çalışmayabilir.


3
Sıralama rasgele nesnelerin dizileri için yardımcı olmayacaktır
Tim Down

Dizi sıralanmamışsa, 1000 uzunluk dizisi x 1000 uzunluk dizisi ile kesiştiğinizde yaklaşık 1.000.000 kez döngü yapmanız gerekir
YOU

Sanırım benim fikrimi kaçırdınız, yani JavaScript'teki rastgele nesnelerin doğal sıralama düzeni yok, yani bir dizi rastgele nesneyi sıralamak eşit nesnelerin gruplanmasına neden olmayacak. İşe yaramayan verimli bir algoritmaya sahip olmak iyi değildir.
Tim Down

Ah üzgünüm, "keyfi nesneleri" kaçırdım, evet, haklısın. bu nesne onu sıralayamaz ve algoritma bunlar üzerinde çalışmayabilir.
SİZ

8

@ Atk uygulamasının sıralı ilkel diziler için uygulama performansı .shift yerine .pop kullanılarak iyileştirilebilir.

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 kullanarak bir ölçüt oluşturdum . .Pop kullanmak yaklaşık üç kat daha hızlı.


1
Diğerleri için bir yan not gibi - bu sadece dizgiler için değil sayılar için çalışır.
Izhaki

Eğer (ve aşağıdakilerle aynı) ile değiştirirseniz a[aLast] > b[bLast], bunun dizelerde çalışacağını unutmayın. a[aLast].localeCompare(b[bLast]) > 0else if
andrew

1
Hız farkı, dizilerin boyutuna bağlıdır, çünkü .popO (1) ve .shift()O (n)
Esailija

8

JQuery kullanma :

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

8
Bu da yazılabilir c = $(b).filter(a);, ancak belgelerin yalnızca öğeler için çalıştığını belirttiği için bu tür dizi manipülasyonu için jQuery'ye güvenmenizi tavsiye etmem.
Stryner

1
Bu op sorusuna cevap vermez: "En basit, kütüphane içermeyen kod nedir ..."
Madbreaks

7

Yalnızca dizeleri veya sayıları içeren diziler için, diğer yanıtların bazılarına göre sıralama ile bir şeyler yapabilirsiniz. Keyfi nesne dizilerinin genel durumu için, bunu uzun yoldan yapmaktan kaçınabileceğinizi sanmıyorum. Aşağıdakiler, parametre olarak sağlanan herhangi bir dizi dizinin kesişimini verecektir 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"]; 

Bu sadece nesne kimliğinin tek eşitlik biçimi olduğu durumda işe yarar.
Steven Huwig

Evet, ama bence çoğu insan için doğal görünen bu. Farklı bir eşitlik testi yapmak için alternatif bir fonksiyon eklemek de önemlidir.
Tim Down

Sanırım örneğinizde yanlışlıkla global bir firstArr değişkeni oluşturuyorsunuz.
Jason Jackson

@ JasonJackson: Haklısın, teşekkürler. Değişkeni çağırmak firstArrya da firstArraytüm referansları güncellememek konusunda fikrimi açıkça değiştirdim . Sabit.
Tim Down

7

ES2015 ve Setler kullanarak oldukça kısa. Dize gibi Dizi benzeri değerleri kabul eder ve yinelenenleri kaldırır.

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] ile Setten diziye dönüştürün yinelenen öğeleri kaldıracak, iyi fikir, teşekkürler
V-SHY

1
Bu çözüm sizinkinden iki kez önce sağlandı.
Madbreaks

7

Bir kullanabilirsiniz Setolarak thisArgbir Array#filterve almak Set#hasgeri arama.

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

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


Bunun neden daha fazla oyu olduğunu bilmiyorum. Açıkçası en iyi cevap.
Paul Rooney

5

JavaScript nesnesini kullanarak dizilerden birinde değerlerin bir dizinini oluşturmak için buradaki en küçük olana ( filter / indexOf çözümü ) küçük bir değişiklik , onu O (N * M) 'den "muhtemelen" doğrusal süreye indirecektir. kaynak1 kaynak2

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

Bu en basit çözüm değil ( filter + indexOf'dan daha fazla kod ), ne de en hızlı (muhtemelen intersect_safe () ' den daha sabit bir faktör tarafından daha yavaş ), ama oldukça iyi bir denge gibi görünüyor. Bu açık çok iyi bir performans sağlayan ve önceden sıralanmış girdileri gerektirmez iken, basit tarafı.


5

Herhangi bir sayıda diziyi aynı anda işleyebilen başka bir dizinlenmiş yaklaşım:

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

Yalnızca dize olarak değerlendirilebilen değerler için çalışır ve bunları aşağıdaki gibi bir dizi olarak iletmeniz gerekir:

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

... ancak nesneleri parametre olarak veya kesişilecek öğelerin herhangi biri olarak şeffaf olarak kabul eder (her zaman ortak değer dizisi döndürür). Örnekler:

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

EDIT: Ben sadece, bir bakıma, biraz buggy olduğunu fark ettim.

Yani, girdi dizilerinin kendisinin tekrarlar içeremeyeceğini düşünerek kodladım (verilen örnekte olmadığı gibi).

Ancak girdi dizileri yineleme içeriyorsa, bu yanlış sonuçlar verir. Örnek (aşağıdaki uygulamayı kullanarak):

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

Neyse ki, sadece ikinci seviye endeksleme ekleyerek bunu düzeltmek kolaydır. Yani:

Değişiklik:

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

tarafından:

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

...ve:

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

tarafından:

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

Komple örnek:

// 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
Bu, küçük bir değişiklikle en iyi cevaptır. Sonra var v = satır ekleyin if (typeof v == 'function') continue;ve sonuçlara işlev ekleme atlar. Teşekkürler!
Zsolti

Teşekkürler @Zsolti. Önerinizi eklemiyorum çünkü fonksiyonlara sahip olmak (ve bunu ele almak istediğimiz yol) orijinal sorunun kapsamı dışında. Ama benim düzenlememe bakın: Girdi dizilerinizde tekrarlar varsa, orijinal uygulama buggy olur. Düzenlememde düzelttim. ... Öte yandan, tekrarlama olmayacağından eminseniz, orijinal uygulama biraz daha ucuzdur.
16'da bitifet

... fonksiyonlar hakkında da kesişebilirler: @Zsolti'nin dediği gibi tespit edersek (ile if (typeof v == 'function'), o zaman dizesini ( v.toString()) kullanarak dizin için anahtar olarak kullanabiliriz . bunu yapmak için en kolay yolu bu durumda, son deindexaton da bu durumu tespit etmek ve doğru değeri (fonksiyonu) geri müdahale edilmeli, yerine basit bir mantıksal doğru değerin değer olarak özgün işlevini atamak basitçe Ama..
bitifet

100 elemanlı 30 dizi ile bu ne kadar hızlı olabilir .. CPU kullanımı nasıldır?
aidonsnous

5

Şunları kullanabilirsiniz (IE dışındaki tüm tarayıcılar için):

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

veya IE için:

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

Eğer bir işleve dönüştürebilirseniz iyi olur
avalanche1

@ çığ1 yapı kesişimi = (a1, a2) => a1.filter (e => a2.içerir (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

Verilerinizdeki bazı kısıtlamalarla, bunu doğrusal zamanda yapabilirsiniz!

İçin pozitif tamsayılar : Bir "görülme / görmedim" mantıksala değerlerinin eşlenmesi bir dizi kullanın.

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

Nesneler için benzer bir teknik vardır : kukla bir anahtar alın, dizi1'deki her öğe için "true" olarak ayarlayın, sonra dizi2 öğelerinde bu anahtarı arayın. İşiniz bittiğinde temizleyin.

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

Tabii ki anahtarın daha önce görünmediğinden emin olmalısınız, aksi takdirde verilerinizi yok edeceksiniz ...


Bu arada, herhangi bir dizi diziyi kesmek için kolayca genişletilebilir: boolean'ı tamsayılarla değiştirin ve her görüldüğünde artırın: son turdaki kavşağı kolayca okuyabilirsiniz.
tarulen

İlginç bir çözüm, hoşuma gitti. Diğer birçok çözüm O (n ^ 2) 'dir, ancak bu O (n)' dir. Burada tam sayı kodunu ekledim, burada jsfiddle.net/321juyLu/2 . 3. oldu, ben likey :)
rmcsharry

3

Benim için en iyi olana katkıda bulunacağım:

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

İşlevsel bir yaklaşım, her biri sadece tek bir işle ilgili olan sadece yan etkileri olmayan saf işlevleri kullanmayı düşünmelidir.

Bu kısıtlamalar, ilgili işlevlerin birleştirilebilirliğini ve tekrar kullanılabilirliğini artırır.

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

SetAvantajlı bir arama performansına sahip yerel türün kullanıldığını lütfen unutmayın .

Kopyalardan kaçının

Birinciden defalarca tekrarlanan öğeler Arraykorunurken, ikincisi Arrayde çoğaltılır. Bu istenen davranış olabilir veya olmayabilir. Benzersiz bir sonuca ihtiyacınız varsa sadece dedupeilk argümana başvurun :

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

İstenilen sayıda Arrays'nin kesişimini hesaplayın

Eğer bir keyfi sayıda kavşak hesaplamak istiyorsanız Arrays sadece beste intersectile foldl. İşte bir kolaylık işlevi:

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


Etkileyici işlevsel: Haskell olmadığını onaylamak için iki kez yapmak zorunda kaldı. Tek nitpick: (expr ? true : false)gereksizdir. Sadece exprgerçek booleanlara ihtiyaç duyulmuyorsa, sadece doğru / falsili kullanın.
jose_castro_arnaud

2

Basitlik için:

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

Yararları:

  • kir basit
  • Veri merkezli
  • keyfi sayıda liste için çalışır
  • listelerin keyfi uzunlukları için çalışır
  • keyfi değer türleri için çalışır
  • keyfi sıralama düzeni için çalışır
  • şekli korur (herhangi bir dizideki ilk görünüm sırası)
  • mümkün olduğunda erken çıkar
  • bellek güvenli, İşlev / Dizi prototiplerinde kurcalama eksikliği

Dezavantajları:

  • daha yüksek bellek kullanımı
  • daha yüksek CPU kullanımı
  • azaltma anlayışı gerektirir
  • veri akışının anlaşılmasını gerektirir

Bunu 3D motor veya çekirdek çalışması için kullanmak istemezsiniz, ancak bunu olay tabanlı bir uygulamada çalıştırmakta sorun yaşıyorsanız, tasarımınızın daha büyük sorunları vardır.


2

.reducebir harita oluşturmak .filterve kavşağı bulmak. deleteiçinde .filterikinci diziye benzersiz bir küme gibi davranmamızı sağlar

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

Bu yaklaşımı akıl yürütmek oldukça kolay buluyorum. Sabit zamanda çalışır.


2

Bu muhtemelen list1.filter (n => list2.includes (n)) dışında en basit olanıdır

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


Bu O (nm) zaman karmaşıklığına sahiptir ... bu O (n + m) içinde çözülebilir
alchuang

2

Kesişen birden çok diziyi ele almanız gerekiyorsa:

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]


Peki bu çözüm 100 elemanlı 30 dizi için ne kadar hızlı?
aidonsnous

Bu, Javascript yöntemlerine özgü başka bir şey kullanmaz ve bu nedenle kodun çalışacağı VM bunu olabildiğince optimize etmek için ücretsizdir. Bunu, bu yorumun yaşına göre V8'in modern bir versiyonunda çalıştırdığınız göz önüne alındığında daha hızlı bir çözüm olmadığından oldukça olumluyum.
Belfordz

2

ES6 tarzı basit bir yol.

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

Misal:

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

2

Hatta bu nesnelerin belirli özelliklerine dayalı nesne dizisinin kesişimini algılayabilir bir intesection fonksiyonu yazdım.

Örneğin,

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

ve idmülke dayalı bir kavşak istiyoruz , o zaman çıktı şöyle olmalıdır:

[{id: 20}]

Aynı şekilde, aynı işlev (not: ES6 kodu):

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

ve işlevi şu şekilde çağırabilirsiniz:

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

Ayrıca not: bu işlev, ilk dizi birincil dizi olduğu için kesişim bulur ve böylece kesişim sonucu birincil dizinin işlevi olacaktır.


2

Map.has () ~ O (1) olduğunu varsayarak O (dizi1 + dizi2) zamanıyla bunun daha hızlı olacağını düşünün. Yanlışsa lütfen beni düzeltin.

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

İşte underscore.js uygulaması:

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

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


Undesrcore varsa kötü bir referans değil
Dimitrios Mistriotis
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.