it-swarm.com.de

Gibt es eine Art Hash-Code-Funktion in JavaScript?

Im Grunde versuche ich, ein Objekt mit eindeutigen Objekten, einer Menge, zu erstellen. Ich hatte die geniale Idee, nur ein JavaScript-Objekt mit Objekten für die Eigenschaftsnamen zu verwenden. Sowie,

set[obj] = true;

Das funktioniert bis zu einem gewissen Punkt. Es funktioniert hervorragend mit Zeichenfolgen und Zahlen, aber bei anderen Objekten scheinen alle auf denselben Wert zu "hacken" und auf dieselbe Eigenschaft zuzugreifen. Gibt es eine Möglichkeit, einen eindeutigen Hashwert für ein Objekt zu generieren? Wie machen Strings und Zahlen das gleiche Verhalten?

127
Boog

JavaScript-Objekte können nur Strings als Schlüssel verwenden (alles andere wird in einen String umgewandelt).

Alternativ können Sie ein Array pflegen, das die betreffenden Objekte indiziert, und deren Indexzeichenfolge als Referenz auf das Objekt verwenden. Etwas wie das:

var ObjectReference = [];
ObjectReference.Push(obj);

set['ObjectReference.' + ObjectReference.indexOf(obj)] = true;

Offensichtlich ist es ein wenig wortreich, aber Sie könnten ein paar Methoden schreiben, die damit umgehen, und alles abwägen, was Sie wollen.

Bearbeiten:

Ihre Vermutung ist Tatsache - dies ist ein in JavaScript definiertes Verhalten - insbesondere eine toString-Konvertierung, dh Sie können eine eigene toString-Funktion für das Objekt definieren, das als Eigenschaftsname verwendet wird. - olliej

Dies führt zu einem weiteren interessanten Punkt. Sie können eine toString-Methode für die Objekte definieren, die Sie hashen möchten, und die ihren Hash-Bezeichner bilden können.

31
eyelidlessness

Wenn Sie eine hashCode () - Funktion wie Java in JavaScript wünschen, ist dies Ihre:

String.prototype.hashCode = function(){
    var hash = 0;
    for (var i = 0; i < this.length; i++) {
        var character = this.charCodeAt(i);
        hash = ((hash<<5)-hash)+character;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
}

Dies ist der Implementierungsweg in Java (bitweiser Operator).

45
KimKha

Dies erreichen Sie am einfachsten, indem Sie jedem Ihrer Objekte eine eigene, eindeutige toString-Methode geben:

(function() {
    var id = 0;

    /*global MyObject */
    MyObject = function() {
        this.objectId = '<#MyObject:' + (id++) + '>';
        this.toString= function() {
            return this.objectId;
        };
    };
})();

Ich hatte das gleiche Problem und dieses Problem wurde für mich mit minimalem Aufwand perfekt gelöst. Es war viel einfacher, einen fetten Java-Stil Hashtable erneut zu implementieren und den Objektklassen equals() und hashCode() hinzuzufügen. Stellen Sie einfach sicher, dass Sie nicht auch einen String '<#MyObject: 12> in Ihren Hash einfügen, da sonst der Eintrag für Ihr vorhandenes Objekt mit dieser ID gelöscht wird.

Jetzt sind alle meine Hashes total kalt. Ich habe auch vor wenigen Tagen einen Blogeintrag über dieses genaue Thema gepostet.

31
Daniel X Moore

Was Sie beschrieben haben, wird von Harmony WeakMaps abgedeckt, Teil der ECMAScript 6 Spezifikation (nächste Version von JavaScript). Das heißt: Eine Menge, in der die Schlüssel beliebig sein können (einschließlich undefiniert) und nicht auflistbar sind.

Dies bedeutet, dass Sie keinen Verweis auf einen Wert erhalten können, es sei denn, Sie haben einen direkten Verweis auf den Schlüssel (ein beliebiges Objekt!), Der mit dem Wert verknüpft ist. Dies ist für eine Reihe von Gründen für die Implementierung von Engines in Bezug auf Effizienz und Speicherbereinigung wichtig, aber es ist auch super cool, da neue Semantiken wie revokierbare Zugriffsberechtigungen und das Übergeben von Daten möglich sind, ohne dass der Datensender verfügbar gemacht wird.

Von MDN :

var wm1 = new WeakMap(),
    wm2 = new WeakMap();
var o1 = {},
    o2 = function(){},
    o3 = window;

wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // A value can be anything, including an object or a function.
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // Keys and values can be any objects. Even WeakMaps!

wm1.get(o2); // "azerty"
wm2.get(o2); // Undefined, because there is no value for o2 on wm2.
wm2.get(o3); // Undefined, because that is the set value.

wm1.has(o2); // True
wm2.has(o2); // False
wm2.has(o3); // True (even if the value itself is 'undefined').

wm1.has(o1);   // True
wm1.delete(o1);
wm1.has(o1);   // False

WeakMaps sind im aktuellen Firefox, Chrome und Edge verfügbar. Sie werden auch in Node v7 und in v6 mit dem Flag --harmony-weak-maps unterstützt.

18
user748221

Die Lösung, die ich gewählt habe, ähnelt der von Daniel, aber anstatt eine Objektfabrik zu verwenden und den toString zu überschreiben, füge ich den Hash explizit dem Objekt hinzu, wenn es zum ersten Mal über eine getHashCode-Funktion angefordert wird. Ein bisschen chaotisch, aber besser für meine Bedürfnisse :)

Function.prototype.getHashCode = (function(id) {
    return function() {
        if (!this.hashCode) {
            this.hashCode = '<hash|#' + (id++) + '>';
        }
        return this.hashCode;
    }
}(0));
18
theGecko

In meiner spezifischen Situation kümmere ich mich nur um die Gleichheit des Objekts, soweit Schlüssel und Grundwerte gelten. Die Lösung, die für mich funktionierte, bestand darin, das Objekt in seine JSON-Darstellung zu konvertieren und als Hash zu verwenden. Es gibt Einschränkungen wie die Reihenfolge der Schlüsseldefinitionen, die möglicherweise inkonsistent sind. Aber wie ich schon sagte, es funktionierte für mich, weil diese Objekte alle an einem Ort erzeugt wurden.

var hashtable = {};

var myObject = {a:0,b:1,c:2};

var hash = JSON.stringify(myObject);
// '{"a":0,"b":1,"c":2}'

hashtable[hash] = myObject;
// {
//   '{"a":0,"b":1,"c":2}': myObject
// }
11
ijmacd

Die JavaScript-Spezifikation definiert den Zugriff auf indizierte Eigenschaften als eine toString-Konvertierung für den Indexnamen. Zum Beispiel,

myObject[myProperty] = ...;

ist das gleiche wie

myObject[myProperty.toString()] = ...;

Dies ist wie in JavaScript notwendig

myObject["someProperty"]

ist das gleiche wie

myObject.someProperty

Und ja, es macht mich auch traurig :-(

8
olliej

Ich habe vor einiger Zeit ein kleines JavaScript-Modul zusammengestellt, um Hashcodes für Strings, Objekte, Arrays usw. zu erstellen. (Ich habe es gerade in GitHub :) festgelegt.

Verwendungszweck:

Hashcode.value("stackoverflow")
// -2559914341
Hashcode.value({ 'site' : "stackoverflow" })
// -3579752159
8
Metalstorm

In ECMAScript 6 gibt es jetzt eine Set, die so funktioniert, wie Sie möchten: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

Es ist bereits in den neuesten Versionen von Chrome, FF und IE11 verfügbar.

7
Daniel X Moore

Referenz: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

sie können das Es6-Symbol verwenden, um einen eindeutigen Schlüssel zu erstellen und auf das Objekt zuzugreifen . Jeder von Symbol () zurückgegebene Symbolwert ist eindeutig. Ein Symbolwert kann als Bezeichner für Objekteigenschaften verwendet werden. Dies ist der einzige Zweck des Datentyps. 

var obj = {};

obj[Symbol('a')] = 'a';
obj[Symbol.for('b')] = 'b';
obj['c'] = 'c';
obj.d = 'd';
4
Khalid Azam

Hier ist meine einfache Lösung, die eine eindeutige ganze Zahl zurückgibt.

function hashcode(obj) {
    var hc = 0;
    var chars = JSON.stringify(obj).replace(/\{|\"|\}|\:|,/g, '');
    var len = chars.length;
    for (var i = 0; i < len; i++) {
        // Bump 7 to larger prime number to increase uniqueness
        hc += (chars.charCodeAt(i) * 7);
    }
    return hc;
}
2
Timothy Perez

Meine Lösung führt eine statische Funktion für das globale Object-Objekt ein.

(function() {
    var lastStorageId = 0;

    this.Object.hash = function(object) {
        var hash = object.__id;

        if (!hash)
             hash = object.__id = lastStorageId++;

        return '#' + hash;
    };
}());

Ich denke, das ist bei anderen Objektbearbeitungsfunktionen in JavaScript bequemer.

1
Johnny

Neben der Antwort auf Augenlidlosigkeit gibt es hier eine Funktion, die eine reproduzierbare, eindeutige ID für jedes Objekt zurückgibt:

var uniqueIdList = [];
function getConstantUniqueIdFor(element) {
    // HACK, using a list results in O(n), but how do we hash e.g. a DOM node?
    if (uniqueIdList.indexOf(element) < 0) {
        uniqueIdList.Push(element);
    }
    return uniqueIdList.indexOf(element);
}

Wie Sie sehen, wird für das Nachschlagen eine Liste verwendet, die sehr ineffizient ist. Dies ist jedoch das Beste, was ich im Moment finden könnte.

0
cburgmer

Wenn Sie eindeutige Werte in einem Suchobjekt haben möchten, können Sie Folgendes tun:

Suchobjekt erstellen

var lookup = {};

Einrichten der Hashcode-Funktion

function getHashCode(obj) {
    var hashCode = '';
    if (typeof obj !== 'object')
        return hashCode + obj;
    for (var prop in obj) // No hasOwnProperty needed
        hashCode += prop + getHashCode(obj[prop]); // Add key + value to the result string
    return hashCode;
}

Objekt

var key = getHashCode({ 1: 3, 3: 7 });
// key = '1337'
lookup[key] = true;

Array

var key = getHashCode([1, 3, 3, 7]);
// key = '01132337'
lookup[key] = true;

Andere Arten

var key = getHashCode('StackOverflow');
// key = 'StackOverflow'
lookup[key] = true;

Endergebnis

{ 1337: true, 01132337: true, StackOverflow: true }

Beachten Sie, dass getHashCode keinen Wert zurückgibt, wenn das Objekt oder das Array leer ist.

getHashCode([{},{},{}]);
// '012'
getHashCode([[],[],[]]);
// '012'

Dies ist nur mit der @ iMacd-Lösung vergleichbar. getHashCode hat keine JSON-Abhängigkeit.

0
A1rPun

Ich werde versuchen, etwas tiefer als andere Antworten zu gehen.

Selbst wenn JS eine bessere Hash-Unterstützung hätte, würde es nicht alles perfekt zaubern, in vielen Fällen müssen Sie Ihre eigene Hash-Funktion definieren. Zum Beispiel hat Java eine gute Unterstützung für Hashing, aber Sie müssen trotzdem noch ein wenig nachdenken und arbeiten.

Ein Problem ist mit dem Begriff Hash/Hashcode ... es gibt kryptografisches Hashing und nicht-kryptografisches Hashing. Das andere Problem ist, dass Sie verstehen müssen, warum Hashing nützlich ist und wie es funktioniert.

Wenn wir über Hashing in JavaScript oder Java sprechen, sprechen wir meistens über nicht-kryptografisches Hashing, normalerweise über Hashing für Hashmap/Hashtable (es sei denn, wir arbeiten an Authentifizierung oder Passwörtern, die Sie möglicherweise serverseitig mit NodeJS ausführen. ..).

Es hängt davon ab, welche Daten Sie haben und was Sie erreichen möchten.

Ihre Daten haben eine natürliche "einfache" Einzigartigkeit:

  • Der Hash einer Ganzzahl ist ... die Ganzzahl, da sie einzigartig ist, viel Glück!
  • Der Hash einer Zeichenfolge ... hängt von der Zeichenfolge ab. Wenn die Zeichenfolge einen eindeutigen Bezeichner darstellt, können Sie ihn als Hash betrachten (daher ist kein Hash erforderlich).
  • Alles, was indirekt eine eindeutige ganze Zahl ist, ist der einfachste Fall
  • Dies wird Folgendes berücksichtigen: Hashcode gleich, wenn Objekte gleich sind

Ihre Daten haben eine natürliche "zusammengesetzte" Einzigartigkeit:

  • Mit einem Personenobjekt können Sie beispielsweise einen Hash berechnen, indem Sie Vorname, Nachname, Geburtsdatum usw. verwenden, um zu sehen, wie Java dies tut: Gute Hash-Funktion für Strings , oder verwenden Sie eine andere ID-Information, die billig und eindeutig genug ist für Ihre Verwendung

Sie haben keine Ahnung, wie Ihre Daten aussehen werden:

  • Viel Glück ... Sie könnten in Java-String serialisieren und in einen Hash-Hash schreiben, aber dies kann teuer werden, wenn der String groß ist und Kollisionen nicht vermeiden sowie den Hash einer Ganzzahl (self) sagen kann.

Es gibt keine magisch effiziente Hashmethode für unbekannte Daten. In manchen Fällen ist das ganz einfach, in anderen Fällen müssen Sie möglicherweise zweimal darüber nachdenken. Selbst wenn JavaScript/ECMAScript mehr Unterstützung hinzufügt, gibt es keine magische Sprachlösung für dieses Problem.

In der Praxis braucht man zwei Dinge: genug Einzigartigkeit, genug Geschwindigkeit

Darüber hinaus ist es großartig zu haben: "Hashcode gleich, wenn Objekte gleich sind"

0

Basierend auf dem Titel können wir starke Hashes mit js generieren. Mit js können wir einen eindeutigen Hash aus einem Objekt, einem Array von Parametern, einem String oder was auch immer generieren.

Später zur Indizierung vermeiden Sie mögliche Übereinstimmungsfehler, während Sie einen Index aus den Parametern abrufen können (vermeiden Sie das Durchsuchen/Schleifen des Objekts usw.):

async function H(m) {
  const msgUint8 = new TextEncoder().encode(m)                       
  const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8)          
  const hashArray = Array.from(new Uint8Array(hashBuffer))                    
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
  console.log(hashHex)
}

/* Examples ----------------------- */
H("An obscure ....")
H(JSON.stringify( {"hello" : "world"} ))
H(JSON.stringify( [54,51,54,47] ))

https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string

0
NVRM

Wenn Sie wirklich ein festgelegtes Verhalten wünschen (ich gehe von Java-Kenntnissen aus), werden Sie schwer eine Lösung in JavaScript finden. Die meisten Entwickler empfehlen einen eindeutigen Schlüssel, um jedes Objekt darzustellen. Dies unterscheidet sich jedoch nicht von set, da Sie zwei identische Objekte mit jeweils einem eindeutigen Schlüssel erhalten können. Die Java-API prüft auf doppelte Werte durch Vergleichen von Hash-Code-Werten und nicht von Schlüsseln. Da in JavaScript keine Hash-Code-Wertedarstellung von Objekten vorhanden ist, ist es fast unmöglich, dies zu tun. Sogar die Prototype JS-Bibliothek erkennt diesen Mangel an, wenn sie sagt:

"Hash kann als ein Assoziatives Array betrachtet werden, das eindeutige Schlüssel An Werte bindet (die nicht unbedingt Eindeutig sind) ..."

http://www.prototypejs.org/api/hash

0
user4903

Ich kombinierte die Antworten aus Augenlidlosigkeit und KimKha.

Der folgende ist ein anglejs-Dienst und unterstützt Zahlen, Zeichenfolgen und Objekte.

exports.Hash = () => {
  let hashFunc;
  function stringHash(string, noType) {
    let hashString = string;
    if (!noType) {
      hashString = `string${string}`;
    }
    var hash = 0;
    for (var i = 0; i < hashString.length; i++) {
        var character = hashString.charCodeAt(i);
        hash = ((hash<<5)-hash)+character;
        hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
  }

  function objectHash(obj, exclude) {
    if (exclude.indexOf(obj) > -1) {
      return undefined;
    }
    let hash = '';
    const keys = Object.keys(obj).sort();
    for (let index = 0; index < keys.length; index += 1) {
      const key = keys[index];
      const keyHash = hashFunc(key);
      const attrHash = hashFunc(obj[key], exclude);
      exclude.Push(obj[key]);
      hash += stringHash(`object${keyHash}${attrHash}`, true);
    }
    return stringHash(hash, true);
  }

  function Hash(unkType, exclude) {
    let ex = exclude;
    if (ex === undefined) {
      ex = [];
    }
    if (!isNaN(unkType) && typeof unkType !== 'string') {
      return unkType;
    }
    switch (typeof unkType) {
      case 'object':
        return objectHash(unkType, ex);
      default:
        return stringHash(String(unkType));
    }
  }

  hashFunc = Hash;

  return Hash;
};

Verwendungsbeispiel:

Hash('hello world'), Hash('hello world') == Hash('hello world')
Hash({hello: 'hello world'}), Hash({hello: 'hello world'}) == Hash({hello: 'hello world'})
Hash({hello: 'hello world', goodbye: 'adios amigos'}), Hash({hello: 'hello world', goodbye: 'adios amigos'}) == Hash({goodbye: 'adios amigos', hello: 'hello world'})
Hash(['hello world']), Hash(['hello world']) == Hash(['hello world'])
Hash(1), Hash(1) == Hash(1)
Hash('1'), Hash('1') == Hash('1')

Ausgabe

432700947 true
-411117486 true
1725787021 true
-1585332251 true
1 true
-1881759168 true

Erklärung

Wie Sie sehen können, ist das Herzstück des Services die von KimKha erstellte Hash-Funktion. Ich habe den Zeichenfolgen Typen hinzugefügt, so dass die Struktur des Objekts auch den endgültigen Hash-Wert beeinflussen würde.

augenlidlosigkeitsobjektvergleich wird verwendet, um eine unbegrenzte Rekursion durch sich selbst referenzierende Objekte zu verhindern.

Verwendungszweck

Ich habe diesen Dienst erstellt, damit ich einen Fehlerdienst haben kann, auf den mit Objekten zugegriffen wird. So kann ein Dienst einen Fehler mit einem bestimmten Objekt registrieren und ein anderer kann feststellen, ob Fehler gefunden wurden.

dh

JsonValidation.js

ErrorSvc({id: 1, json: '{attr: "not-valid"}'}, 'Invalid Json Syntax - key not double quoted');

UserOfData.js

ErrorSvc({id: 1, json: '{attr: "not-valid"}'});

Dies würde zurückkehren:

['Invalid Json Syntax - key not double quoted']

Während

ErrorSvc({id: 1, json: '{"attr": "not-valid"}'});

Das würde wiederkommen

[]
0

Wenn Sie Objekte als Schlüssel verwenden möchten, müssen Sie deren toString-Methode überschreiben, wie bereits erwähnt wurde. Die Hash-Funktionen, die verwendet wurden, sind alle in Ordnung, sie funktionieren jedoch nur für dieselben Objekte, nicht für gleiche Objekte.

Ich habe eine kleine Bibliothek geschrieben, die Hashes aus Objekten erstellt, die Sie leicht für diesen Zweck verwenden können. Die Objekte können sogar eine andere Reihenfolge haben, die Hashwerte sind gleich. Intern können Sie verschiedene Typen für Ihren Hash verwenden (djb2, md5, sha1, sha256, sha512, ripemd160).

Hier ein kleines Beispiel aus der Dokumentation:

var hash = require('es-hash');

// Save data in an object with an object as a key
Object.prototype.toString = function () {
    return '[object Object #'+hash(this)+']';
}

var foo = {};

foo[{bar: 'foo'}] = 'foo';

/*
 * Output:
 *  foo
 *  undefined
 */
console.log(foo[{bar: 'foo'}]);
console.log(foo[{}]);

Das Paket kann entweder im Browser und in Node-Js verwendet werden.

Repository: https://bitbucket.org/tehrengruber/es-js-hash

0
darthmatch