Was entspricht JavaScript einem C # HashSet?


75

Ich habe eine Liste mit einigen tausend Ganzzahlschlüsseln. Das einzige, was ich mit dieser Liste tun muss, ist zu sagen, ob ein bestimmter Wert in der Liste enthalten ist oder nicht.

Für C # würde ich a verwenden HashSet, um diese Suche schnell zu machen. Was ist das JavaScript-Äquivalent?


Minimale Unterstützungsstufe: IE 9+, jQuery (aktuell)


10
In der Regel ein Objekt.
pswg


Ich bin damit einverstanden, dass es sich um ein Duplikat handelt (obwohl nicht jeder die Verwendung einer Karte als Set aufgreift ..), aber die Antworten in der verknüpften Frage übersehen viele der unten aufgeführten Eigenheiten und Hinweise.
user2864740

5
Kein Duplikat - ein HashSet und ein Dictionary sind sehr unterschiedlich. Mit einem Wörterbuch können Sie das Objekt abrufen. Mit einem HashSet können Sie das Objekt nicht abrufen, sondern nur, ob eines vorhanden ist.
Kees C. Bakker

3
Wie kommt es, dass buchstäblich keine dieser Antworten einen der Hauptpunkte von HashSet <T> verfehlt? Es sind nur eindeutige Elemente zulässig.
user9993

Antworten:


78

Unter der Haube wird das JavaScript-Objekt mit einer Hash-Tabelle implementiert. Also Key:Valuewäre dein Paar(your integer):true

Eine zeitlich konstante Suchfunktion könnte implementiert werden als:

var hash = {
  1:true,
  2:true,
  7:true
  //etc...
};

var checkValue = function(value){
  return hash[value] === true;
};


checkValue(7); // => true
checkValue(3); // => false

3
Nett. Eine weitere Sache, die Sie beachten sollten: Wenn Sie für die Iteration (num in hash) etwas tun möchten , ist die num (als Schlüssel gespeichert) hier vom Typ string.
Rongsir

Auch ein (Wert in Hash) würde ausreichen, um nur die Eigenschaft mit dem Nullwert zu registrieren - nicht einmal dem wahren Booleschen Wert - und mit dem Lösch-Hash [Wert] zu entfernen
Demetris Leptos

3
In Javascript gibt es ein Set-Objekt, das den Anforderungen dieser Person besser entspricht.
Remydib

4
Die ECMAScript-Spezifikation definiert die Set.has () -Funktion eindeutig als Iteration durch das gesamte Set, bis eine Übereinstimmung gefunden wird. Dies scheint nicht dem erklärten Ziel des OP zu entsprechen, schnell nach HashSet in anderen Sprachen zu suchen.
JD Sandifer

4
@JDSandifer: In derselben Spezifikation heißt es auch: "Set-Objekte müssen entweder mithilfe von Hash-Tabellen oder anderen Mechanismen implementiert werden, die im Durchschnitt Zugriffszeiten bereitstellen, die sublinear zur Anzahl der Elemente in der Sammlung sind. Die in diesen Set-Objekten verwendeten Datenstrukturen Die Spezifikation soll nur die erforderliche beobachtbare Semantik von Set-Objekten beschreiben. Sie soll kein tragfähiges Implementierungsmodell sein. " In modernem JavaScript Setund Mapsind die besten Dinge zu verwenden, wenn Sie ein Set oder eine Karte benötigen.
jmrk

96

Tatsächlich bietet JavaScript ein Set-Objekt , das recht einfach zu verwenden ist:

var set = new Set();
set.add(1);
set.add(2);

set.has(1)    // true

Leider ist es nicht mit IE9 kompatibel.


14
Dies sollte die bevorzugte Antwort im Jahr 2018 sein.
Anders Tornblad

1
@LexJacobs: Lesen Sie das Intro zu Set-Objekte in dieser Spezifikation. Implementationen sind erforderlich haben , schneller als O (n) Lookup, in der Praxis , dass Mittel O (lügt n) oder O (1).
jmrk

34

Verwenden Sie ein Objekt. Gehen Sie folgendermaßen vor, um dem Set einen Schlüssel hinzuzufügen:

object[key] = true;

Um zu testen, ob sich ein Schlüssel im Set befindet, gehen Sie wie folgt vor:

if (object.hasOwnProperty(key)) { ... }

Gehen Sie wie folgt vor, um einen Schlüssel aus dem Set zu entfernen:

delete object[key]

1
unset? Der Operator ist deletein JS!
Gsnedders

Danke, wollte gerade die Syntax überprüfen
Barmar

3
Der richtige Weg, um zu testen, ob sich ein Schlüssel im Set befindet, ist die Verwendung object.hasOwnProperty(key). Ansonsten , "toString" in object == true.
Bart

5
hasOwnProperty ist langsam und was ist, wenn es einen Schlüssel namens hasOwnProperty gibt? benutze o = Object.create (null); Um das "Hashset" anstelle eines Literal zu erstellen, können Sie anstelle der methodischen hasOwnProperty () den schnellen Eigenschaftszugriff verwenden, um nach der Schlüsselzugehörigkeit zu suchen, und müssen sich dabei keine Gedanken über Eigenschaftskollisionen machen.
Dandavis

8

Sie können nur ein reguläres JavaScript-Objekt und das Schlüsselwort 'in' verwenden, um festzustellen, ob dieses Objekt einen bestimmten Schlüssel hat.

var myObj = {
  name: true,
  age: true
}

'name' in myObj //returns true;
'height' in myObj // returns false;

Oder wenn Sie wissen, dass Ihr Objekt Schlüssel enthält, die möglicherweise in JavaScript-Objekteigenschaften erstellt wurden, verwenden Sie ...

var myObj = {
  name: true,
  age: true
}

myObj.hasOwnProperty('name') //returns true;
myObj.hasOwnProperty('height') // returns false;

'toString' in myObj // gibt true zurück
Barmar

2
und was ist, wenn es einen Schlüssel namens "hasOwnProperty" gibt?
Dandavis

24
Dann bewerten Sie Ihr Leben neu.
Tyler McGinnis

@ TylerMcGinnis lol - Ich denke, es würde immer noch funktionieren - aber ja, überdenken Sie, warum Sie eine Eigenschaft namens hasOwnProperty haben .. und verwenden Sie trotzdem "in"
Demetris Leptos

1
Wir scheinen vergessen zu haben, dass der Fall, ein Objekt als Schlüssel zu haben, nicht behandelt wird. Es sei denn, Sie identifizieren alle Ihre Objekte schrittweise mit '$ id' (ich bin von Newton inspiriert) oder '__ id __', damit Sie dies dann tun können Verwenden Sie das als Schlüssel, wenn Sie ein Objekt sind. hash [isNotPrimitive (Schlüssel)? Schlüssel ['$ id']: Schlüssel]
Demetris Leptos

1

Ich habe die Lösungen gelesen und einige ausprobiert. Nachdem object[key]ich versucht hatte, die Methode anzuwenden , stellte ich fest, dass sie nicht funktionieren würde. Ich wollte ein HashSet, das HTML-Elemente speichern kann. Beim Hinzufügen dieser Objekte keywurde das in eine Zeichenfolge übersetzt, sodass ich ein eigenes Set basierend auf jQuery entwickelte. Es unterstützt add, remove, containsund clear.

var HashSet = function () {

    var set = [];

    this.add = function (obj) {
        if (!this.contains(obj)) {
            set.push(obj);
        }
    };

    this.remove = function (obj) {
        set = jQuery.grep(set, function (value) {
            return value !== obj;
        });
    };

    this.clear = function () {
        set = [];
    };

    this.contains = function (obj) {
        return $.inArray(obj, set) > -1;
    };

    this.isEmpty = function () {
        return set.length === 0;
    };
};

Hinweis
Wenn Sie $('#myElement')dem Set etwas Ähnliches hinzufügen, sollten Sie das echte HTML-Element hinzufügen $('#myElement')[0]. Oh ... und wenn Sie eine Liste der geänderten Steuerelemente behalten möchten, verwenden Sie den Namen des Elements (gab mir ein Problem mit den :radioSteuerelementen).

Hinweis 2
Ich denke, das object[key]könnte für Ihre ganzen Zahlen schneller sein.

Hinweis 3
Wenn Sie nur Zahlen oder Zeichenfolgen speichern möchten , ist dieser Satz schneller:

var HashSet = function () {

    var set = {};

    this.add = function (key) {
        set[key] = true;
    };

    this.remove = function (key) {
        delete set[key];
    };

    this.clear = function () {
        set = {};
    };

    this.contains = function (key) {
        return set.hasOwnProperty(key);
    };

    this.isEmpty = function () {
        return jQuery.isEmptyObject(set);
    };
};

3
Dies ist überhaupt kein HashSet ... Es erfüllt keines der Kriterien, die man von einem HashSet erwarten würde
Andrew Whitaker

Es hat nicht die O1-Leistung eines HashSet, das stimmt. Aber es ahmt die HashSet-Funktionen von C # nach.
Kees C. Bakker

3
Nein, es ahmt die ICollection-Schnittstelle msdn.microsoft.com/en-us/library/92t2ye13%28v=vs.110%29.aspx nach, die HashSet implementiert. Der Punkt von HashSet ist normalerweise die O (1) -Suchzeit, während die erste Implementierung, die Sie bereitstellen, im Verhalten der Liste ähnlicher ist und O (N) ist.
Oskar Berggren

1
@OskarBerggren - Das in Note3 bereitgestellte Set sollte damit kein Problem haben und O1 sein.
Kees C. Bakker

1

Map oder wenn keine Notwendigkeit besteht, WeakMap zu iterieren

let m1=new Map();

m1.set('isClosed',false);
m1.set('isInitialized',false);

m1.set('isClosed',true);

m1.forEach(function(v,k)
{
    console.log(`${k}=${v}`);
});

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.