Was ist der schnellste oder eleganteste Weg, um einen Satzunterschied mit Javascript-Arrays zu berechnen?

Lesezeit: 9 Minuten

Was ist der schnellste oder eleganteste Weg um einen Satzunterschied
Mattball

Lassen A und B zwei Sätze sein. Ich suche Ja wirklich schnelle oder elegante Möglichkeiten, die Mengendifferenz zu berechnen (A - B oder A \B, je nach Wunsch) zwischen ihnen. Die beiden Sätze werden, wie der Titel sagt, als Javascript-Arrays gespeichert und bearbeitet.

Anmerkungen:

  • Gecko-spezifische Tricks sind in Ordnung
  • Ich würde es vorziehen, mich an native Funktionen zu halten (aber ich bin offen für eine leichte Bibliothek, wenn sie viel schneller ist)
  • Ich habe gesehen, aber nicht getestet, JS.Set (siehe vorherigen Punkt)

Bearbeiten: Mir ist ein Kommentar zu Mengen aufgefallen, die doppelte Elemente enthalten. Wenn ich “Set” sage, beziehe ich mich auf die mathematische Definition, was (unter anderem) bedeutet, dass sie keine doppelten Elemente enthalten.

  • Was ist diese „Set-Differenz“-Terminologie, die Sie verwenden? Ist das von C++ oder so?

    – Josh Stodola

    12. November 2009 um 15:44 Uhr

  • Was ist in Ihren Sets? Abhängig von der Art, auf die Sie abzielen (z. B. Zahlen), kann eine Satzdifferenz berechnet werden Ja wirklich schnell und elegant. Wenn Ihre Sets (sagen wir) DOM-Elemente enthalten, werden Sie mit einer Verlangsamung stecken bleiben indexOf Implementierung.

    – Halbmondfrisch

    12. November 2009 um 15:53 ​​Uhr

  • @Crescent: Meine Sätze enthalten Zahlen – Entschuldigung, dass ich sie nicht angegeben habe. @Josh: Es ist die Standardmengenoperation in der Mathematik (en.wikipedia.org/wiki/Set_%28mathematics%29#Complements)

    – Mattball

    12. November 2009 um 16:30 Uhr

  • @JoshStodola das ist der mathematische Notation für Set-Differenz

    – Pat

    12. August 2014 um 0:21 Uhr

  • @MattBall Nein, das habe ich gesehen. Aber Joshs Frage war gültig und unbeantwortet, also habe ich sie beantwortet 🙂

    – Pat

    14. August 2014 um 21:19 Uhr

Was ist der schnellste oder eleganteste Weg um einen Satzunterschied
Benutzer187291

wenn ich nicht weiß, ob dies am effektivsten ist, aber vielleicht am kürzesten

A = [1, 2, 3, 4];
B = [1, 3, 4, 7];

diff = A.filter(function(x) { return B.indexOf(x) < 0 })

console.log(diff);

Aktualisiert auf ES6:

A = [1, 2, 3, 4];
B = [1, 3, 4, 7];

diff = A.filter(x => !B.includes(x) );

console.log(diff);

  • +1: nicht die effizienteste Lösung, aber auf jeden Fall kurz und lesbar

    – Christoph

    12. November 2009 um 17:37 Uhr

  • Hinweis: array.filter wird nicht browserübergreifend unterstützt (z. B. nicht im IE). Es scheint @Matt egal zu sein, da er sagte, dass “Gecko-spezifische Tricks in Ordnung sind”, aber ich denke, es ist erwähnenswert.

    – Eric Brechemier

    13. November 2009 um 16:44 Uhr

  • Das ist sehr langsam. O(|A| * |B|)

    – glebm

    8. April 2013 um 0:37 Uhr

  • @EricBréchemier Dies wird jetzt unterstützt (seit IE 9). Array.prototype.filter ist eine standardmäßige ECMAScript-Funktion.

    – Quentin Roy

    21. Juni 2016 um 8:14 Uhr

  • In ES6 könnten Sie verwenden !B.includes(x) anstatt B.indexOf(x) < 0 🙂

    – c24w

    26. Juni 2017 um 11:27 Uhr

Was ist der schnellste oder eleganteste Weg um einen Satzunterschied
Mailand

Nun, 7 Jahre später, mit ES6-Set Objekt ist es ganz einfach (aber immer noch nicht so kompakt wie Pythons A - B) und angeblich schneller als indexOf für große Arrays:

console.clear();
let a = new Set([1, 2, 3, 4]);
let b = new Set([5, 4, 3, 2]);


let a_minus_b = new Set([...a].filter(x => !b.has(x)));
let b_minus_a = new Set([...b].filter(x => !a.has(x)));
let a_intersect_b = new Set([...a].filter(x => b.has(x))); 

console.log([...a_minus_b]) // {1}
console.log([...b_minus_a]) // {5}
console.log([...a_intersect_b]) // {2,3,4}

  • Auch erheblich schneller als indexOf für große Arrays.

    – Estus-Kolben

    14. September 2016 um 6:34 Uhr

  • Warum JavaScript-Sets keine union/intersect/difference eingebaut haben, ist mir schleierhaft…

    – SwiftsNamensvetter

    27. November 2016 um 4:50 Uhr

  • Ich stimme vollkommen zu; Dies sollten Primitive auf niedrigerer Ebene sein, die in der js-Engine implementiert sind. Es ist mir auch ein Rätsel…

    – Raffael

    6. Januar 2017 um 19:37 Uhr

  • @SwiftsNamesake Es gibt einen Vorschlag für festgelegte integrierte Methoden, über die hoffentlich im Januar 2018 gesprochen wird github.com/tc39/agendas/blob/master/2018/01.md.

    – John

    23. Januar 2018 um 6:07 Uhr

1645919713 753 Was ist der schnellste oder eleganteste Weg um einen Satzunterschied
Christoph

Sie können ein Objekt als Karte verwenden, um ein lineares Scannen zu vermeiden B für jedes Element von A wie in der Antwort von user187291:

function setMinus(A, B) {
    var map = {}, C = [];

    for(var i = B.length; i--; )
        map[B[i].toSource()] = null; // any other value would do

    for(var i = A.length; i--; ) {
        if(!map.hasOwnProperty(A[i].toSource()))
            C.push(A[i]);
    }

    return C;
}

Der Nicht-Standard toSource() Methode wird verwendet, um eindeutige Eigenschaftsnamen zu erhalten; Wenn alle Elemente bereits eindeutige Zeichenfolgendarstellungen haben (wie es bei Zahlen der Fall ist), können Sie den Code beschleunigen, indem Sie das löschen toSource() Anrufungen.

1645919714 37 Was ist der schnellste oder eleganteste Weg um einen Satzunterschied
Koblas

Wenn Sie sich viele dieser Lösungen ansehen, eignen sie sich gut für kleine Fälle. Aber wenn Sie sie auf eine Million Elemente aufblähen, wird die Zeitkomplexität albern.

 A.filter(v => B.includes(v))

Das sieht aus wie eine O(N^2)-Lösung. Da es eine O(N)-Lösung gibt, lassen Sie uns sie verwenden, können Sie leicht ändern, dass sie kein Generator ist, wenn Sie mit Ihrer JS-Laufzeit nicht auf dem neuesten Stand sind.

    function *setMinus(A, B) {
      const setA = new Set(A);
      const setB = new Set(B);

      for (const v of setB.values()) {
        if (!setA.delete(v)) {
            yield v;
        }
      }

      for (const v of setA.values()) {
        yield v;
      }
    }

    a = [1,2,3];
    b = [2,3,4];

    console.log(Array.from(setMinus(a, b)));

Dies ist zwar etwas komplexer als viele der anderen Lösungen, aber wenn Sie große Listen haben, wird dies viel schneller sein.

Werfen wir einen kurzen Blick auf den Leistungsunterschied, indem wir ihn mit einem Satz von 1.000.000 zufälligen ganzen Zahlen zwischen 0 und 10.000 ausführen, sehen wir die folgenden Leistungsergebnisse.

setMinus time =  181 ms
    diff time =  19099 ms

function buildList(count, range) {
  result = [];
  for (i = 0; i < count; i++) {
    result.push(Math.floor(Math.random() * range))
  }
  return result;
}

function *setMinus(A, B) {
  const setA = new Set(A);
  const setB = new Set(B);

  for (const v of setB.values()) {
    if (!setA.delete(v)) {
        yield v;
    }
  }

  for (const v of setA.values()) {
    yield v;
  }
}

function doDiff(A, B) {
  return A.filter(function(x) { return B.indexOf(x) < 0 })
}

const listA = buildList(100_000, 100_000_000); 
const listB = buildList(100_000, 100_000_000); 

let t0 = process.hrtime.bigint()

const _x = Array.from(setMinus(listA, listB))

let t1 = process.hrtime.bigint()

const _y = doDiff(listA, listB)

let t2 = process.hrtime.bigint()

console.log("setMinus time = ", (t1 - t0) / 1_000_000n, "ms");
console.log("diff time = ", (t2 - t1) / 1_000_000n, "ms");

1645919715 194 Was ist der schnellste oder eleganteste Weg um einen Satzunterschied
Perhelion

Das kürzeste mit jQuery ist:

var A = [1, 2, 3, 4];
var B = [1, 3, 4, 7];

var diff = $(A).not(B);

console.log(diff.toArray());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

  • Dies gibt ein Objekt der Differenz zurück.

    – Drew Baker

    10. September 2015 um 18:31 Uhr

  • jQuery not funktioniert ab 3.0.0-rc1 nicht mehr mit generischen Objekten. Sehen github.com/jquery/jquery/issues/3147

    – Marc-André Lafortune

    9. Juni 2016 um 15:38 Uhr

  • Es ist keine gute Idee, eine Abhängigkeit von einer ca. 70.000 Bibliothek eines Drittanbieters hinzuzufügen gerade um dies zu tun, da dasselbe in nur wenigen Codezeilen erreicht werden kann, wie in den anderen Antworten hier gezeigt. Wenn Sie jQuery jedoch bereits in Ihrem Projekt verwenden, funktioniert dies problemlos.

    – Finite Looper

    5. Dezember 2016 um 14:24 Uhr

  • Obwohl dieser Ansatz weniger Code hat, liefert er keine Erklärung der Raum- und Zeitkomplexität der unterschiedlichen Algorithmen und der Datenstruktur, die er verwendet, um das Verfahren auszuführen. Es ist für Entwickler verboten, die Software ohne Bewertung zu entwickeln, wenn die Datenskalierung erhöht wird oder der Speicher begrenzt ist. Wenn Sie einen solchen Ansatz mit einem großen Datensatz verwenden, bleibt die Leistung möglicherweise unbekannt, bis weitere Untersuchungen des Quellcodes durchgeführt werden.

    – Abfahrtski

    18. Januar 2017 um 3:24 Uhr


  • Dies gibt nur die Menge (in diesem Fall 2) der Elemente von A zurück, die nicht in B enthalten sind. Das Konvertieren von 2 in ein Array ist sinnlos …

    – Alex

    2. März 2018 um 12:29 Uhr

Wenn Sie verwenden Sets, es kann ganz einfach und performant sein:

function setDifference(a, b) {
  return new Set(Array.from(a).filter(item => !b.has(item)));
}

Seit Sets verwenden Hash-Funktionen* unter der Haube, die has Funktion ist viel schneller als indexOf (Dies ist wichtig, wenn Sie beispielsweise mehr als 100 Artikel haben).

  • Dies gibt ein Objekt der Differenz zurück.

    – Drew Baker

    10. September 2015 um 18:31 Uhr

  • jQuery not funktioniert ab 3.0.0-rc1 nicht mehr mit generischen Objekten. Sehen github.com/jquery/jquery/issues/3147

    – Marc-André Lafortune

    9. Juni 2016 um 15:38 Uhr

  • Es ist keine gute Idee, eine Abhängigkeit von einer ca. 70.000 Bibliothek eines Drittanbieters hinzuzufügen gerade um dies zu tun, da dasselbe in nur wenigen Codezeilen erreicht werden kann, wie in den anderen Antworten hier gezeigt. Wenn Sie jQuery jedoch bereits in Ihrem Projekt verwenden, funktioniert dies problemlos.

    – Finite Looper

    5. Dezember 2016 um 14:24 Uhr

  • Obwohl dieser Ansatz weniger Code hat, liefert er keine Erklärung der Raum- und Zeitkomplexität der unterschiedlichen Algorithmen und der Datenstruktur, die er verwendet, um das Verfahren auszuführen. Es ist für Entwickler verboten, die Software ohne Bewertung zu entwickeln, wenn die Datenskalierung erhöht wird oder der Speicher begrenzt ist. Wenn Sie einen solchen Ansatz mit einem großen Datensatz verwenden, bleibt die Leistung möglicherweise unbekannt, bis weitere Untersuchungen des Quellcodes durchgeführt werden.

    – Abfahrtski

    18. Januar 2017 um 3:24 Uhr


  • Dies gibt nur die Menge (in diesem Fall 2) der Elemente von A zurück, die nicht in B enthalten sind. Das Konvertieren von 2 in ein Array ist sinnlos …

    – Alex

    2. März 2018 um 12:29 Uhr

1645919716 443 Was ist der schnellste oder eleganteste Weg um einen Satzunterschied
Eric Brechemier

Ich würde das Array B hashen und dann Werte aus dem Array A behalten, die nicht in B vorhanden sind:

function getHash(array){
  // Hash an array into a set of properties
  //
  // params:
  //   array - (array) (!nil) the array to hash
  //
  // return: (object)
  //   hash object with one property set to true for each value in the array

  var hash = {};
  for (var i=0; i<array.length; i++){
    hash[ array[i] ] = true;
  }
  return hash;
}

function getDifference(a, b){
  // compute the difference a\b
  //
  // params:
  //   a - (array) (!nil) first array as a set of values (no duplicates)
  //   b - (array) (!nil) second array as a set of values (no duplicates)
  //
  // return: (array)
  //   the set of values (no duplicates) in array a and not in b, 
  //   listed in the same order as in array a.

  var hash = getHash(b);
  var diff = [];
  for (var i=0; i<a.length; i++){
    var value = a[i];
    if ( !hash[value]){
      diff.push(value);
    }
  }
  return diff;
}

  • das ist genau derselbe Algorithmus, den ich vor einer halben Stunde gepostet habe

    – Christoph

    12. November 2009 um 17:15 Uhr

  • @Christoph: du hast recht… das ist mir nicht aufgefallen. Ich finde meine Implementierung jedoch einfacher zu verstehen 🙂

    – Eric Brechemier

    13. November 2009 um 16:41 Uhr

  • Ich denke, es ist besser, den Unterschied außerhalb von getDifference zu berechnen, damit er mehrmals wiederverwendet werden kann. Vielleicht optional so: getDifference(a, b, hashOfB)wenn es nicht übergeben wird, wird es berechnet, andernfalls wird es so wie es ist wiederverwendet.

    – Christoph Roussy

    12. April 2017 um 11:54 Uhr

869660cookie-checkWas ist der schnellste oder eleganteste Weg, um einen Satzunterschied mit Javascript-Arrays zu berechnen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy