Esiste un tipo di funzione di codice hash in JavaScript?


150

Fondamentalmente, sto cercando di creare un oggetto di oggetti unici, un set. Ho avuto la geniale idea di usare semplicemente un oggetto JavaScript con oggetti per i nomi delle proprietà. Ad esempio,

set[obj] = true;

Questo funziona, fino a un certo punto. Funziona alla grande con stringhe e numeri, ma con altri oggetti, sembrano tutti "hash" allo stesso valore e accedere alla stessa proprietà. Esiste un modo per generare un valore hash univoco per un oggetto? Come lo fanno stringhe e numeri, posso ignorare lo stesso comportamento?


32
Il motivo per cui tutti gli oggetti 'hash' sullo stesso valore è perché non hai sovrascritto i loro metodi toString. Poiché le chiavi devono essere stringhe, il metodo toString viene chiamato automaticamente per ottenere una chiave valida in modo che tutti gli oggetti vengano convertiti nella stessa stringa predefinita: "[oggetto oggetto]".
dal

4
JSON.stringify(obj)o obj.toSource()potrebbe funzionare per te a seconda del problema e della piattaforma di destinazione.
AnnanFay,

4
@Annan JSON.stringify (obj) converte letteralmente l'oggetto (intero) in una stringa. Quindi, in pratica, dovresti semplicemente copiare l'oggetto su se stesso. Questo è inutile, uno spreco di spazio e non ottimale.
Metalstorm,

1
@Metalstorm True, ecco perché dipende dal tuo problema. Quando ho trovato questa domanda tramite Google, la mia soluzione finale era quella di chiamare to Source () su oggetti. Un altro metodo sarebbe semplicemente quello di utilizzare un hash convenzionale sull'origine.
AnnanFay,

@Annan, toSourcenon lavorare in Chrome a proposito
Pacerier,

Risposte:


35

Gli oggetti JavaScript possono utilizzare solo stringhe come chiavi (qualsiasi altra cosa viene convertita in stringa).

In alternativa, è possibile mantenere un array che indicizza gli oggetti in questione e utilizzare la sua stringa di indice come riferimento all'oggetto. Qualcosa come questo:

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

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

Ovviamente è un po 'prolisso, ma potresti scrivere un paio di metodi che lo gestiscono e ottengono e impostano tutti volenti o nolenti.

Modificare:

La tua ipotesi è un fatto - questo è un comportamento definito in JavaScript - in particolare si verifica una conversione toString nel senso che puoi definire la tua funzione toString sull'oggetto che verrà usato come nome della proprietà. - Olliej

Questo fa apparire un altro punto interessante; puoi definire un metodo toString sugli oggetti che vuoi hash e che può formare il loro identificatore hash.


un'altra opzione sarebbe quella di dare a ciascun oggetto un valore casuale in quanto è hash - forse un numero casuale + tick totali - quindi avere una serie di funzioni per aggiungere / rimuovere l'oggetto dall'array.
Sugendran,

4
Questo fallirà se aggiungi lo stesso oggetto due volte. Penserà che è diverso.
Daniel X Moore,

"Questo fallirà se aggiungi lo stesso oggetto due volte. Penserà che è diverso." Buon punto. Una soluzione potrebbe essere quella di sottoclassare Array per ObjectReference, collegando un controllo duplicato in push (). Non ho tempo di modificare questa soluzione ora, ma spero che me lo ricorderò più tardi.
mancanza di palpebre

8
Mi piace questa soluzione, perché non necessita di proprietà aggiuntive nell'oggetto. Ma sta diventando problematico, se provi ad avere un bidone della spazzatura pulito. Nel tuo approccio salverà l'oggetto anche se i suoi altri riferimenti sono già stati eliminati. Ciò può causare problemi in applicazioni più grandi.
Johnny,

35
Qual è il punto di hashing degli oggetti se ogni volta che ti riferisci ad essi hai bisogno di una scansione lineare di un array?
Bordaigorl,

57

Se vuoi una funzione hashCode () come Java in JavaScript, questa è la tua:

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

Questo è il modo di implementazione in Java (operatore bit a bit).

Nota che hashCode potrebbe essere positivo e negativo, ed è normale, vedi HashCode che fornisce valori negativi . Quindi, potresti considerare di usare Math.abs()insieme a questa funzione.


5
questo crea -hash, non perfetto
qodeninja

2
@KimKha charè una parola riservata in JS e potrebbe causare alcuni problemi. Qualche altro nome sarebbe migliore.
szeryf,

16
@qodeninja dice chi? È la prima volta che sento una simile affermazione. Puoi collegarti a qualche fonte? Gli hash vengono di solito calcolati utilizzando l'aritmetica di interi di dimensioni fisse e le operazioni sui bit, quindi è prevedibile ottenere risultati positivi o negativi.
szeryf,

7
Picky, ma ... "if (this.length == 0) restituisce l'hash;" è ridondante :) E cambierebbe personalmente "carattere" in "codice".
Metalstorm,

10
@qodeninja e @szeryf: devi solo stare attento a come lo usi. Ad esempio, ho provato a fare pickOne["helloo".hashCode() % 20]un array pickOnecon 20 elementi. Ho capito undefinedperché il codice hash è negativo, quindi questo è un esempio in cui qualcuno (io) ha assunto implicitamente codici hash positivi.
Jim Pivarski,

31

Il modo più semplice per farlo è quello di dare a ciascuno dei tuoi oggetti il ​​suo toStringmetodo unico :

(function() {
    var id = 0;

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

Ho avuto lo stesso problema e questo mi ha risolto perfettamente con il minimo sforzo, ed è stato molto più semplice implementare un po 'di grasso stile Java Hashtablee aggiungere equals()ehashCode() alle tue classi di oggetti. Assicurati solo di non attaccare una stringa '<#MyObject: 12> nel tuo hash o che cancellerà la voce per il tuo oggetto uscente con quell'id.

Ora tutti i miei hash sono completamente freddi. Ho anche pubblicato un post sul blog qualche giorno fa su questo argomento esatto .


28
Ma questo manca il punto. Java ha equals()e hashCode()quindi due oggetti equivalenti hanno lo stesso valore di hash. L'uso del metodo sopra indica che ogni istanza di MyObjectavrà una stringa univoca, il che significa che dovrai mantenere un riferimento a quell'oggetto per recuperare il valore corretto dalla mappa. Avere una chiave non ha senso, perché non ha nulla a che fare con l'unicità di un oggetto. toString()Sarà necessario implementare una funzione utile per il tipo specifico di oggetto che si sta utilizzando come chiave.
sethro,

@sethro è possibile implementare toStringper gli oggetti in modo tale da mappare direttamente una relazione di equivalenza in modo che due oggetti creino la stessa stringa se sono considerati "uguali".
Daniel X Moore,

3
Giusto, e questo è l' unico modo corretto da usare toString()per permetterti di usare Objectcome Set. Penso di aver frainteso la tua risposta nel tentativo di fornire una soluzione generica per evitare di scrivere un toString()equivalente equals()o hashCode()caso per caso.
sethro,

3
Dowvoted. Questo non è ciò che un hashcode è, vedere le mie risposte a: stackoverflow.com/a/14953738/524126 e un vero implementazione di un codice hash: stackoverflow.com/a/15868654/524126
MetalStorm

5
@Metalstorm la domanda non stava ponendo un hashcode "vero", ma piuttosto come utilizzare correttamente un oggetto come set in JavaScript.
Daniel X Moore,

20

Quello che hai descritto è coperto da Harmony WeakMaps , parte della specifica ECMAScript 6 (prossima versione di JavaScript). Cioè: un set in cui le chiavi possono essere qualsiasi cosa (incluso indefinito) ed è non enumerabile.

Ciò significa che è impossibile ottenere un riferimento a un valore a meno che non si abbia un riferimento diretto alla chiave (qualsiasi oggetto!) Che si collega ad esso. È importante per una serie di motivi di implementazione del motore relativi all'efficienza e alla garbage collection, ma è anche fantastico perché consente nuove semantiche come autorizzazioni di accesso revocabili e passaggio di dati senza esporre il mittente dei dati.

Da 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

Le mappe deboli sono disponibili negli attuali Firefox, Chrome e Edge. Sono inoltre supportati in Nodo v7 e in v6 con la --harmony-weak-mapsbandiera.


1
Qual è la differenza tra questi e Map?
smac89,

@ smac89 WeakMap ha dei limiti: 1) Accetta solo oggetti come chiavi 2) Nessuna proprietà size 3) Nessun iteratore o metodo forOach 4) Nessun metodo clear. La chiave è oggetto - quindi quando l'oggetto verrà eliminato dalla memoria - verranno eliminati anche i dati di WeakMap collegati a questo oggetto. È molto utile quando vogliamo conservare le informazioni, che dovrebbero esistere solo mentre l'oggetto esiste. Quindi WeakMap ha solo metodi: impostare, eliminare per scrivere e ottenere, per leggere
Ekaterina Tokareva

Questo non funziona esattamente correttamente ... var m = new Map();m.set({},"abc"); console.log(m.get({}) //=>undefinedFunziona solo se hai la stessa variabile a cui inizialmente hai fatto riferimento nel comando set. EGvar m = new Map();a={};m.set(a,"abc"); console.log(m.get(a) //=>undefined
Sancarn,

1
@Sancarn Non deve essere la stessa variabile, ma devono puntare allo stesso oggetto. Nel tuo primo esempio hai due oggetti diversi, hanno lo stesso aspetto, ma hanno un indirizzo diverso.
Svish,

1
@Svish buon posto! Anche se ora lo so, forse non avrei fatto allora :)
Sancarn,

19

La soluzione che ho scelto è simile a quella di Daniel, ma anziché utilizzare una factory di oggetti e sovrascrivere toString, aggiungo esplicitamente l'hash all'oggetto quando viene richiesto per la prima volta tramite una funzione getHashCode. Un po 'disordinato, ma migliore per le mie esigenze :)

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

7
Se vuoi andare in questo modo, è molto meglio impostare l'hashCode tramite Object.definePropertycon enumerableset su false, in modo da non interrompere alcun for .. inloop.
Sebastian Nowak,

14

Per la mia situazione specifica, mi preoccupo solo dell'uguaglianza dell'oggetto per quanto riguarda le chiavi e i valori primitivi. La soluzione che ha funzionato per me è stata convertire l'oggetto nella sua rappresentazione JSON e usarlo come hash. Vi sono limitazioni come l'ordine di definizione della chiave potenzialmente incoerente; ma come ho detto, ha funzionato per me perché tutti gli oggetti venivano generati in un unico posto.

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

10

Qualche tempo fa ho messo insieme un piccolo modulo JavaScript per produrre hashcode per stringhe, oggetti, array, ecc. (L'ho appena assegnato a GitHub :))

Uso:

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

Il GC di javascript non si soffoca anche su riferimenti circolari?
Clayton Rabenda,

@Ryan Long Potrei arrivare al punto di dire se hai riferimenti circolari quindi devi refactoring il tuo codice;)
Metalstorm

11
@Metalstorm "allora devi refactificare il tuo codice" Stai scherzando? Ogni coppia padre e figlio dell'elemento DOM costituisce un riferimento circolare.
Chris Middleton,

8
Fa un lavoro scadente con hashing di oggetti che hanno proprietà numeriche, restituendo lo stesso valore in molti casi, cioè var hash1 = Hashcode.value({ a: 1, b: 2 }); var hash2 = Hashcode.value({ a: 2, b: 1 }); console.log(hash1, hash2);registrerà2867874173 2867874173
Julien Bérubé

9

La specifica JavaScript definisce l'accesso alla proprietà indicizzata come l'esecuzione di una conversione toString sul nome dell'indice. Per esempio,

myObject[myProperty] = ...;

equivale a

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

Questo è necessario come in JavaScript

myObject["someProperty"]

equivale a

myObject.someProperty

E sì, mi rende anche triste :-(



5

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

puoi usare il simbolo Es6 per creare una chiave univoca e accedere all'oggetto. Ogni valore di simbolo restituito da Symbol () è univoco. Un valore di simbolo può essere utilizzato come identificatore per le proprietà dell'oggetto; questo è l'unico scopo del tipo di dati.

var obj = {};

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

2
Solo che non c'è davvero un modo per rigenerare il Simbolo let x = Symbol ('a'); let y = Symbol ('a'); console.log (x === y); // restituisce false Quindi Symbol non funziona come un hash.
Richard Collette,

3

Ecco la mia semplice soluzione che restituisce un numero intero univoco.

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
La complessità di questo mina l'intera idea dietro hashCode ()
tuxSlayer

Non lo trovo inutilmente complesso. Ero curioso, però: perché la fase di sostituzione? Le esclusioni sarebbero altrimenti tornate bene su charCodeAt, no?
Greg Pettit,

Peccato a causa di hashcode({a:1, b:2}) === hashcode({a:2, b:1})e molti altri conflitti.
Maaartinus,

3

In base al titolo, possiamo generare hash forti con js, può essere utilizzato per generare un hash univoco da un oggetto, una matrice di parametri, una stringa o altro.

In seguito per l'indicizzazione questo evita ogni possibile errore di corrispondenza, consentendo nel contempo di recuperare un indice dai parametri (evitare di cercare / eseguire il loop dell'oggetto ecc.):

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

L'output di cui sopra nel mio browser, dovrebbe essere uguale anche per te ( è davvero? ):

bf1cf3fe6975fe382ab392ec1dd42009380614be03d489f23601c11413cfca2b
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
d2f209e194045604a3b15bdfd7502898a0e848e4603c5a818bd01da69c00ad19

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


1

La mia soluzione introduce una funzione statica per l' Objectoggetto globale .

(function() {
    var lastStorageId = 0;

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

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

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

Penso che questo sia più conveniente con altre funzioni di manipolazione degli oggetti in JavaScript.


1
Gli oggetti con gli stessi valori interni eseguiranno l'hash su hash diversi, non è ciò che fa un hash (codice).
Metalstorm,

In JavaScript (e penso anche in quasi tutte le altre lingue) due oggetti creati con gli stessi valori interni sono ancora oggetti diversi, poiché il tipo di dati sottostante è rappresentato da una nuova istanza di oggetto ciascuno. jsfiddle.net/h4G9f
Johnny

4
Sì, ma non è quello a cui serve un hashcode, gli hashcode sono usati per verificare l'uguaglianza di uno stato di oggetti. Proprio come un hash, entrano gli stessi input (valori variabili). Quello che stai cercando è un UUID (che è ciò che la tua funzione fornisce).
Metalstorm,

1
Hai ragione. Ho frainteso la domanda. Davvero male, che la risposta accettata non fornisce neanche una buona soluzione.
Johnny,

Prendendo anche la tua funzione, sarei propenso a renderlo più simile a questo: jsfiddle.net/xVSsd Stesso risultato, più breve (LoC + caratteri) e probabilmente un pochino più veloce :)
Metalstorm

1

Proverò ad andare un po 'più in profondità rispetto alle altre risposte.

Anche se JS avesse un supporto di hashing migliore, non avrebbe magicamente tutto perfettamente sotto controllo, in molti casi dovrai definire la tua funzione di hash. Ad esempio, Java ha un buon supporto di hashing, ma devi ancora pensare e fare un po 'di lavoro.

Un problema è con il termine hash / hashcode ... c'è hash crittografico e hash non crittografico. L'altro problema è che devi capire perché l'hashing è utile e come funziona.

Quando parliamo di hash in JavaScript o Java la maggior parte delle volte parliamo di hash non crittografico, di solito di hash per hashmap / hashtable (a meno che non stiamo lavorando su autenticazione o password, che potresti fare sul lato server usando NodeJS. ..).

Dipende da quali dati hai e cosa vuoi ottenere.

I tuoi dati hanno una naturale "semplice" unicità:

  • L'hash di un numero intero è ... il numero intero, in quanto è unico, beato te!
  • L'hash di una stringa ... dipende dalla stringa, se la stringa rappresenta un identificatore univoco, è possibile considerarlo come un hash (quindi non è necessario alcun hash).
  • Qualunque cosa che sia indirettamente praticamente un intero unico è il caso più semplice
  • Questo rispetterà: hashcode uguale se gli oggetti sono uguali

I tuoi dati hanno una naturale unicità "composita":

  • Ad esempio con un oggetto persona, puoi calcolare un hash usando il nome, il cognome, la data di nascita, ... guarda come fa Java: buona funzione hash per le stringhe o usa altre informazioni ID che sono economiche e uniche abbastanza per il tuo caso d'uso

Non hai idea di quali saranno i tuoi dati:

  • Buona fortuna ... potresti serializzare su stringa e hash lo stile Java, ma potrebbe essere costoso se la stringa è grande e non eviterà le collisioni, come dire l'hash di un numero intero (self).

Non esiste una tecnica di hashing magicamente efficiente per dati sconosciuti, in alcuni casi è abbastanza semplice, in altri casi potresti dover pensarci due volte. Quindi, anche se JavaScript / ECMAScript aggiungono ulteriore supporto, non esiste una soluzione di linguaggio magico per questo problema.

In pratica hai bisogno di due cose: abbastanza unicità, abbastanza velocità

Inoltre, è bello avere: "hashcode uguale se gli oggetti sono uguali"


0

Se vuoi davvero impostare il comportamento (vado dalla conoscenza di Java), allora sarai molto difficile trovare una soluzione in JavaScript. La maggior parte degli sviluppatori raccomanderà una chiave univoca per rappresentare ogni oggetto, ma questo è diverso da quello impostato, in quanto è possibile ottenere due oggetti identici ciascuno con una chiave univoca. L'API Java svolge il compito di verificare la presenza di valori duplicati confrontando i valori del codice hash, non le chiavi, e poiché non esiste una rappresentazione del valore del codice hash degli oggetti in JavaScript, diventa quasi impossibile fare lo stesso. Anche la libreria Prototype JS ammette questo difetto, quando dice:

"L'hash può essere pensato come un array associativo, che lega chiavi uniche a valori (che non sono necessariamente unici) ..."

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


0

Oltre alla risposta per le ciglia, ecco una funzione che restituisce un ID univoco riproducibile per qualsiasi oggetto:

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

Come puoi vedere usa un elenco per la ricerca che è molto inefficiente, tuttavia è il migliore che potrei trovare per ora.


0

Se vuoi usare oggetti come chiavi devi sovrascrivere il loro metodo toString, come alcuni già menzionati qui. Le funzioni hash che sono state usate vanno bene, ma funzionano solo per gli stessi oggetti, non per oggetti uguali.

Ho scritto una piccola libreria che crea hash dagli oggetti, che puoi usare facilmente per questo scopo. Gli oggetti possono anche avere un ordine diverso, gli hash saranno gli stessi. Internamente puoi usare diversi tipi per il tuo hash (djb2, md5, sha1, sha256, sha512, ripemd160).

Ecco un piccolo esempio dalla documentazione:

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[{}]);

Il pacchetto può essere utilizzato nel browser e in Node-Js.

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


0

Se vuoi avere valori univoci in un oggetto di ricerca, puoi fare qualcosa del genere:

Creazione di un oggetto di ricerca

var lookup = {};

Impostazione della funzione hashcode

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

Oggetto

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

Vettore

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

Altri tipi

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

Risultato finale

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

Si noti che getHashCodenon restituisce alcun valore quando l'oggetto o la matrice sono vuoti

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

Questo è simile alla soluzione @ijmacd solo getHashCodenon ha la JSONdipendenza.


Devi avere un problema con i riferimenti circolari con quello
tuxSlayer

@tuxSlayer Grazie per avermelo fatto notare. Potresti facilmente espandere questo codice con le tue esigenze, ma spero che l'idea sia in qualche modo chiara :)
A1rPun

Questo produrrà chiavi molto lunghe per oggetti di grandi dimensioni che potrebbero compromettere la memoria e le prestazioni piuttosto duramente
Gershom,

0

Ho combinato le risposte di palpebra e KimKha.

Quello che segue è un servizio angularjs e supporta numeri, stringhe e oggetti.

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

Esempio di utilizzo:

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')

Produzione

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

Spiegazione

Come puoi vedere, il cuore del servizio è la funzione hash creata da KimKha. Ho aggiunto tipi alle stringhe in modo che la struttura dell'oggetto abbia un impatto anche sul valore finale dell'hash. Le chiavi sono sottoposte a hash per impedire collisioni di array | oggetti.

la comparazione di oggetti senza palpebre viene utilizzata per prevenire la ricorsione infinita da oggetti autoreferenziali.

uso

Ho creato questo servizio in modo da poter avere un servizio di errore a cui si accede con oggetti. In modo che un servizio possa registrare un errore con un determinato oggetto e un altro può determinare se sono stati rilevati errori.

vale a dire

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

Ciò restituirebbe:

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

Mentre

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

Questo sarebbe tornato

[]

0

Basta usare la proprietà segreta nascosta con il defineProperty enumerable: false

Funziona molto velocemente :

  • La prima lettura uniqueId: 1.257.500 ops / s
  • Tutti gli altri: 309.226.485 ops / s
var nextObjectId = 1
function getNextObjectId() {
    return nextObjectId++
}

var UNIQUE_ID_PROPERTY_NAME = '458d576952bc489ab45e98ac7f296fd9'
function getObjectUniqueId(object) {
    if (object == null) {
        return null
    }

    var id = object[UNIQUE_ID_PROPERTY_NAME]

    if (id != null) {
        return id
    }

    if (Object.isFrozen(object)) {
        return null
    }

    var uniqueId = getNextObjectId()
    Object.defineProperty(object, UNIQUE_ID_PROPERTY_NAME, {
        enumerable: false,
        configurable: false,
        writable: false,
        value: uniqueId,
    })

    return uniqueId
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.