Was ist der schnellste oder eleganteste Weg, um einen Satzunterschied mit Javascript-Arrays zu berechnen?
Lesezeit: 9 Minuten
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.
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
Mailand
Nun, 7 Jahre später, mit ES6-Set Objekt ist es ganz einfach (aber immer noch nicht so kompakt wie PythonsA - 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…
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.
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");
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());
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).
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
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
8696600cookie-checkWas ist der schnellste oder eleganteste Weg, um einen Satzunterschied mit Javascript-Arrays zu berechnen?yes
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