Existe-t-il une fonction de code de hachage en JavaScript?


150

En gros, j'essaye de créer un objet d'objets uniques, un ensemble. J'ai eu la brillante idée d'utiliser simplement un objet JavaScript avec des objets pour les noms de propriétés. Tel que,

set[obj] = true;

Cela fonctionne, jusqu'à un certain point. Cela fonctionne très bien avec des chaînes et des nombres, mais avec d'autres objets, ils semblent tous "hacher" à la même valeur et accéder à la même propriété. Existe-t-il un moyen de générer une valeur de hachage unique pour un objet? Comment les chaînes et les nombres le font, puis-je remplacer le même comportement?


32
La raison pour laquelle les objets sont tous «hachés» à la même valeur est que vous n'avez pas remplacé leurs méthodes toString. Puisque les clés doivent être des chaînes, la méthode toString est automatiquement appelée pour obtenir une clé valide afin que tous vos objets soient convertis en la même chaîne par défaut: "[objet objet]".
alanning le

4
JSON.stringify(obj)ou obj.toSource()peut fonctionner pour vous en fonction du problème et de la plate-forme cible.
AnnanFay

4
@Annan JSON.stringify (obj) convertit littéralement l'objet (entier) en une chaîne. Donc, vous copieriez simplement l'objet sur lui-même. C'est inutile, un gaspillage d'espace et non optimal.
Metalstorm

1
@Metalstorm True, c'est pourquoi cela dépend de votre problème. Quand j'ai trouvé cette question via Google, ma solution finale a été d'appeler toSource () sur des objets. Une autre méthode consisterait simplement à utiliser un hachage conventionnel sur la source.
AnnanFay

@Annan, toSourcene fonctionne pas dans Chrome btw
Pacerier

Réponses:


35

Les objets JavaScript ne peuvent utiliser que des chaînes comme clés (tout le reste est converti en chaîne).

Vous pouvez également conserver un tableau qui indexe les objets en question et utiliser sa chaîne d'index comme référence à l'objet. Quelque chose comme ça:

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

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

De toute évidence, c'est un peu verbeux, mais vous pouvez écrire quelques méthodes pour le gérer et obtenir et définir tout bon gré mal gré.

Éditer:

Votre hypothèse est un fait - il s'agit d'un comportement défini dans JavaScript - en particulier une conversion toString se produit, ce qui signifie que vous pouvez définir votre propre fonction toString sur l'objet qui sera utilisé comme nom de propriété. - olliej

Cela soulève un autre point intéressant; vous pouvez définir une méthode toString sur les objets que vous souhaitez hacher, et qui peuvent former leur identifiant de hachage.


une autre option serait de donner à chaque objet une valeur aléatoire car c'est un hachage - peut-être un nombre aléatoire + total de graduations - puis avoir un ensemble de fonctions pour ajouter / supprimer l'objet du tableau.
Sugendran

4
Cela échouera si vous ajoutez le même objet deux fois. Il pensera que c'est différent.
Daniel X Moore

"Cela échouera si vous ajoutez deux fois le même objet. Il pensera qu'il est différent." Bon point. Une solution pourrait être de sous-classer Array pour ObjectReference, en accrochant une vérification en double dans push (). Je n'ai pas le temps de modifier cette solution maintenant, mais j'espère que je m'en souviendrai plus tard.
paupière

8
J'aime cette solution, car elle ne nécessite aucune propriété supplémentaire dans l'objet. Mais cela devient problématique si vous essayez d'avoir un ramasse-miettes propre. Dans votre approche, il enregistrera l'objet bien que ses autres références aient déjà été supprimées. Cela peut entraîner des problèmes dans les applications plus volumineuses.
Johnny

35
Quel est l'intérêt de hacher les objets si à chaque fois que vous y faites référence, vous avez besoin d'un balayage linéaire d'un tableau?
Bordaigorl

57

Si vous voulez une fonction hashCode () comme celle de Java en JavaScript, c'est la vôtre:

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

C'est le mode d'implémentation en Java (opérateur bit à bit).

Veuillez noter que hashCode peut être positif et négatif, et c'est normal, voir HashCode donnant des valeurs négatives . Donc, vous pouvez envisager d'utiliser Math.abs()avec cette fonction.


5
cela crée -hash, pas parfait
qodeninja

2
@KimKha charest un mot réservé dans JS et peut causer des problèmes. Un autre nom serait mieux.
szeryf

16
@qodeninja dit qui? C'est la première fois que j'entends une telle affirmation. Pouvez-vous créer un lien vers une source? Les hachages sont généralement calculés à l'aide d'une arithmétique d'entiers de taille fixe et d'opérations sur les bits, il faut donc s'attendre à obtenir des résultats positifs ou négatifs.
szeryf

7
Picky, mais ... "if (this.length == 0) return hash;" est redondant :) Et changerait personnellement "caractère" en "code".
Metalstorm

10
@qodeninja et @szeryf: il faut juste faire attention à son utilisation. Par exemple, j'ai essayé de faire pickOne["helloo".hashCode() % 20]pour un tableau pickOneavec 20 éléments. J'ai obtenu undefinedparce que le code de hachage est négatif, c'est donc un exemple dans lequel quelqu'un (moi) a implicitement supposé des codes de hachage positifs.
Jim Pivarski

31

Le moyen le plus simple de le faire est de donner à chacun de vos objets sa propre toStringméthode unique :

(function() {
    var id = 0;

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

J'ai eu le même problème et cela l'a parfaitement résolu pour moi avec un minimum de tracas, et c'était beaucoup plus facile que de réimplémenter un style Java gras Hashtableet d'ajouter equals()et hashCode()à vos classes d'objets. Assurez-vous simplement que vous ne collez pas également une chaîne '<#MyObject: 12> dans votre hachage ou cela effacera l'entrée de votre objet existant avec cet identifiant.

Maintenant, tous mes hashes sont totalement froids. Je viens également de publier une entrée de blog il y a quelques jours sur ce sujet précis .


28
Mais cela manque le point entier. Java a equals()et hashCode()pour que deux objets équivalents aient la même valeur de hachage. L'utilisation de la méthode ci-dessus signifie que chaque instance de MyObjectaura une chaîne unique, ce qui signifie que vous devrez conserver une référence à cet objet pour récupérer la valeur correcte de la carte. Avoir une clé n'a pas de sens, car cela n'a rien à voir avec l'unicité d'un objet. Une toString()fonction utile devra être implémentée pour le type spécifique d'objet que vous utilisez comme clé.
sethro

@sethro vous pouvez implémenter le toStringpour les objets de telle sorte qu'il mappe directement à une relation d'équivalence de sorte que deux objets créent la même chaîne ssils sont considérés comme "égaux".
Daniel X Moore

3
Exact, et c'est la seule manière correcte d'utiliser toString()pour vous permettre d'utiliser un Objectfichier Set. Je pense que j'ai mal compris votre réponse en essayant de fournir une solution générique pour éviter d'écrire un toString()équivalent equals()ou hashCode()au cas par cas.
sethro

3
Dow a voté. Ce n'est pas ce qu'est un hashcode, voir mes réponses à: stackoverflow.com/a/14953738/524126 Et une véritable implémentation d'un hashcode: stackoverflow.com/a/15868654/524126
Metalstorm

5
@Metalstorm la question ne portait pas sur un "vrai" hashcode, mais plutôt sur la façon d'utiliser avec succès un objet en tant qu'ensemble en JavaScript.
Daniel X Moore

20

Ce que vous avez décrit est couvert par Harmony WeakMaps , qui fait partie de la spécification ECMAScript 6 (prochaine version de JavaScript). C'est-à-dire: un ensemble où les clés peuvent être n'importe quoi (y compris indéfini) et ne sont pas énumérables.

Cela signifie qu'il est impossible d'obtenir une référence à une valeur à moins d'avoir une référence directe à la clé (n'importe quel objet!) Qui y est liée. C'est important pour un tas de raisons d'implémentation de moteur liées à l'efficacité et au ramassage des ordures, mais c'est aussi super cool car cela permet une nouvelle sémantique comme des autorisations d'accès révocables et la transmission de données sans exposer l'expéditeur de données.

De 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 est disponible dans Firefox, Chrome et Edge actuels. Ils sont également pris en charge dans Node v7 et dans v6 avec l' --harmony-weak-mapsindicateur.


1
Quelle est la différence entre ceux-ci et Map?
smac89

@ smac89 WeakMap a des limites: 1) Prend uniquement des objets comme clés 2) Aucune propriété de taille 3) Aucun itérateur ou méthode forEach 4) Aucune méthode clear. La clé est un objet - donc lorsque l'objet sera supprimé de la mémoire - les données de WeakMap connectées à cet objet seront également supprimées. C'est très utile lorsque nous voulons conserver les informations, qui ne devraient exister que tant que l'objet existe. Donc WeakMap n'a que des méthodes: définir, supprimer pour écrire et obtenir, a pour lire
Ekaterina Tokareva

Cela ne fonctionne pas exactement correctement ... var m = new Map();m.set({},"abc"); console.log(m.get({}) //=>undefinedCela ne fonctionne que si vous avez la même variable que vous avez initialement référencée dans la commande set. EGvar m = new Map();a={};m.set(a,"abc"); console.log(m.get(a) //=>undefined
Sancarn

1
@Sancarn Il n'est pas nécessaire que ce soit la même variable, mais ils doivent pointer vers le même objet. Dans votre premier exemple, vous avez deux objets différents, ils se ressemblent, mais ils ont une adresse différente.
Svish

1
@Svish bon spot! Bien que je le sache maintenant, je ne l'ai peut-être pas fait à l'époque :)
Sancarn

19

La solution que j'ai choisie est similaire à celle de Daniel, mais plutôt que d'utiliser une fabrique d'objets et de remplacer le toString, j'ajoute explicitement le hachage à l'objet lorsqu'il est demandé pour la première fois via une fonction getHashCode. Un peu en désordre, mais mieux pour mes besoins :)

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

7
Si vous voulez aller de cette façon, il est préférable de définir hashCode via Object.definePropertyavec enumerableset to false, afin de ne planter aucune for .. inboucle.
Sebastian Nowak

14

Pour ma situation spécifique, je ne me soucie que de l'égalité de l'objet en ce qui concerne les clés et les valeurs primitives. La solution qui a fonctionné pour moi a été de convertir l'objet en sa représentation JSON et de l'utiliser comme hachage. Il existe des limites telles que l'ordre de définition des clés pouvant être incohérent; mais comme je l'ai dit, cela a fonctionné pour moi parce que ces objets étaient tous générés au même endroit.

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

J'ai mis en place un petit module JavaScript il y a quelque temps pour produire des hashcodes pour des chaînes, des objets, des tableaux, etc. (je viens de le valider dans GitHub :))

Usage:

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

Le GC de javascript ne s'étouffe-t-il pas aussi sur les références circulaires?
Clayton Rabenda

@Ryan Long Je pourrais aller jusqu'à dire que si vous avez des références circulaires, vous devez refactoriser votre code;)
Metalstorm

11
@Metalstorm "alors vous devez refactoriser votre code" Vous plaisantez? Chaque paire d'élément DOM parent et enfant constitue une référence circulaire.
Chris Middleton le

8
Il fait un mauvais travail de hachage des objets qui ont des propriétés numériques, renvoyant la même valeur dans de nombreux cas, c'est à dire var hash1 = Hashcode.value({ a: 1, b: 2 }); var hash2 = Hashcode.value({ a: 2, b: 1 }); console.log(hash1, hash2);se connecter2867874173 2867874173
Julien Bérubé

9

La spécification JavaScript définit l'accès aux propriétés indexées comme effectuant une conversion toString sur le nom de l'index. Par exemple,

myObject[myProperty] = ...;

est le même que

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

C'est nécessaire comme en JavaScript

myObject["someProperty"]

est le même que

myObject.someProperty

Et oui, ça me rend triste aussi :-(


9

Dans ECMAScript 6, il y a maintenant un Setqui fonctionne comme vous le souhaitez: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

Il est déjà disponible dans les derniers Chrome, FF et IE11.


1
Cela devrait être la meilleure réponse en 2016. Si vous utilisez Babel, vous pouvez utiliser Set selon la spécification ES6 et il sera polyfilled automatiquement dans la sortie ES5. babeljs.io/docs/learn-es2015/#map-set-weak-map-weak-set
atroberts20

5

Référence: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

vous pouvez utiliser le symbole Es6 pour créer une clé unique et un objet d'accès. Chaque valeur de symbole renvoyée par Symbol () est unique. Une valeur de symbole peut être utilisée comme identificateur pour les propriétés d'objet; c'est le seul objectif du type de données.

var obj = {};

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

2
Sauf qu'il n'y a pas vraiment de moyen de régénérer le symbole let x = Symbol ('a'); soit y = Symbole ('a'); console.log (x === y); // renvoie false Donc Symbol ne fonctionne pas comme un hachage.
Richard Collette

3

Voici ma solution simple qui renvoie un entier unique.

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 complexité de cela sape toute l'idée derrière hashCode ()
tuxSlayer

Je ne trouve pas cela inutilement complexe. J'étais curieux, cependant: pourquoi la phase de remplacement? Les exclusions auraient autrement retourné bien sur charCodeAt, n'est-ce pas?
Greg Pettit

Dommage à cause de hashcode({a:1, b:2}) === hashcode({a:2, b:1})nombreux autres conflits.
maaartinus

3

Sur la base du titre, nous pouvons générer des hachages forts avec js, il peut être utilisé pour générer un hachage unique à partir d'un objet, d'un tableau de paramètres, d'une chaîne ou autre.

Plus tard pour l'indexation, évitez toute erreur de correspondance possible, tout en permettant de récupérer un index à partir des paramètres (évitez de rechercher / boucler l'objet, etc.):

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

La sortie ci-dessus dans mon navigateur, elle devrait être égale pour vous aussi ( est-ce vraiment? ):

bf1cf3fe6975fe382ab392ec1dd42009380614be03d489f23601c11413cfca2b
93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588
d2f209e194045604a3b15bdfd7502898a0e848e4603c5a818bd01da69c00ad19

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


1

Ma solution introduit une fonction statique pour l' Objectobjet global .

(function() {
    var lastStorageId = 0;

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

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

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

Je pense que c'est plus pratique avec d'autres fonctions de manipulation d'objets en JavaScript.


1
Les objets avec les mêmes valeurs internes seront hachés en différents hachages, ce n'est pas ce que fait un hachage (code).
Metalstorm

En JavaScript (et je pense que dans presque tous les autres langages également), deux objets créés avec les mêmes valeurs internes sont toujours des objets différents, car le type de données sous-jacent est représenté par une nouvelle instance d'objet chacun. jsfiddle.net/h4G9f
Johnny

4
Oui mais ce n'est pas à quoi sert un hashcode, les hashcodes sont utilisés pour vérifier l'égalité d'un état d'objets. Tout comme un hachage, les mêmes entrées entrent (valeurs variables), le même hachage sort. Ce que vous recherchez est un UUID (qui est ce que fournit votre fonction).
Metalstorm

1
Vous avez raison. J'ai mal compris la question. Vraiment dommage, que la réponse acceptée ne fournit pas non plus une bonne solution.
Johnny

En prenant également votre fonction, je serais enclin à le faire plus comme ceci: jsfiddle.net/xVSsd Même résultat, plus court (LoC + caractères), et probablement un tout petit peu plus rapide :)
Metalstorm

1

J'essaierai d'aller un peu plus loin que les autres réponses.

Même si JS avait un meilleur support de hachage, il ne hacherait pas tout parfaitement par magie, dans de nombreux cas, vous devrez définir votre propre fonction de hachage. Par exemple, Java a un bon support de hachage, mais vous devez encore réfléchir et faire un peu de travail.

Un problème est avec le terme hash / hashcode ... il y a un hachage cryptographique et un hachage non cryptographique. L'autre problème, c'est que vous devez comprendre pourquoi le hachage est utile et comment cela fonctionne.

Lorsque nous parlons de hachage en JavaScript ou Java la plupart du temps, nous parlons de hachage non cryptographique, généralement de hachage pour hashmap / hashtable (sauf si nous travaillons sur l'authentification ou les mots de passe, ce que vous pourriez faire côté serveur en utilisant NodeJS. ..).

Cela dépend des données dont vous disposez et de ce que vous souhaitez atteindre.

Vos données ont un caractère unique "simple" naturel:

  • Le hachage d'un entier est ... l'entier, car il est unique, chanceux!
  • Le hachage d'une chaîne ... cela dépend de la chaîne, si la chaîne représente un identifiant unique, vous pouvez le considérer comme un hachage (donc pas de hachage nécessaire).
  • Tout ce qui est indirectement à peu près un entier unique est le cas le plus simple
  • Cela respectera: hashcode égal si les objets sont égaux

Vos données ont un caractère unique "composite" naturel:

  • Par exemple, avec un objet personne, vous pouvez calculer un hachage en utilisant le prénom, le nom, la date de naissance, ... voir comment Java le fait: bonne fonction de hachage pour les chaînes , ou utiliser d'autres informations d'identification qui sont assez bon marché et uniques pour votre cas d'utilisation

Vous n'avez aucune idée de ce que seront vos données:

  • Bonne chance ... vous pouvez sérialiser en chaîne et hacher le style Java, mais cela peut coûter cher si la chaîne est grande et cela n'évitera pas les collisions ainsi que le hachage d'un entier (self).

Il n'y a pas de technique de hachage magiquement efficace pour les données inconnues, dans certains cas, c'est assez facile, dans d'autres cas, vous devrez peut-être réfléchir à deux fois. Donc, même si JavaScript / ECMAScript ajoute plus de support, il n'y a pas de solution de langage magique pour ce problème.

En pratique, vous avez besoin de deux choses: assez d'unicité, assez de vitesse

En plus de cela, il est génial d'avoir: "hashcode égal si les objets sont égaux"


0

Si vous voulez vraiment définir un comportement (je me fie à Java), vous aurez du mal à trouver une solution en JavaScript. La plupart des développeurs recommanderont une clé unique pour représenter chaque objet, mais cela ne ressemble pas à un ensemble, en ce sens que vous pouvez obtenir deux objets identiques chacun avec une clé unique. L'API Java effectue le travail de vérification des valeurs en double en comparant les valeurs de code de hachage, pas les clés, et comme il n'y a pas de représentation de la valeur de code de hachage des objets en JavaScript, il devient presque impossible de faire de même. Même la bibliothèque Prototype JS admet cette lacune, quand elle dit:

"Le hachage peut être considéré comme un tableau associatif, liant des clés uniques à des valeurs (qui ne sont pas nécessairement uniques) ..."

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


0

En plus de la réponse sans paupière, voici une fonction qui renvoie un identifiant unique et reproductible pour tout objet:

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

Comme vous pouvez le voir, il utilise une liste de recherche qui est très inefficace, mais c'est le meilleur que je puisse trouver pour le moment.


0

Si vous souhaitez utiliser des objets comme clés, vous devez écraser leur méthode toString, comme certains l'ont déjà mentionné ici. Les fonctions de hachage qui ont été utilisées sont toutes correctes, mais elles ne fonctionnent que pour les mêmes objets et non pour des objets égaux.

J'ai écrit une petite bibliothèque qui crée des hachages à partir d'objets, que vous pouvez facilement utiliser à cette fin. Les objets peuvent même avoir un ordre différent, les hachages seront les mêmes. En interne, vous pouvez utiliser différents types pour votre hachage (djb2, md5, sha1, sha256, sha512, ripemd160).

Voici un petit exemple de la documentation:

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

Le package peut être utilisé dans le navigateur et dans Node-Js.

Dépôt: https://bitbucket.org/tehrengruber/es-js-hash


0

Si vous voulez avoir des valeurs uniques dans un objet de recherche, vous pouvez faire quelque chose comme ceci:

Création d'un objet de recherche

var lookup = {};

Configuration de la fonction de 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;
}

Objet

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

Tableau

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

Autres types

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

Résultat final

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

Notez que getHashCodene renvoie aucune valeur lorsque l'objet ou le tableau est vide

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

Ceci est similaire à la solution @ijmacd mais getHashCoden'a pas de JSONdépendance.


Vous devez avoir un problème avec les références circulaires avec ça
tuxSlayer

@tuxSlayer Merci de me l'avoir fait savoir. Vous pouvez facilement étendre ce code avec vos besoins mais j'espère que l'idée est un peu claire :)
A1rPun

Cela produira des touches très longues pour les gros objets qui pourraient frapper la mémoire et les performances assez durement
Gershom

0

J'ai combiné les réponses de la paupière et de KimKha.

Ce qui suit est un service angularjs et il prend en charge les nombres, les chaînes et les objets.

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

Exemple d'utilisation:

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

Production

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

Explication

Comme vous pouvez le voir, le cœur du service est la fonction de hachage créée par KimKha, j'ai ajouté des types aux chaînes afin que la structure de l'objet ait également un impact sur la valeur de hachage finale.Les clés sont hachées pour éviter les collisions tableau | objet.

La comparaison d'objets sans paupière est utilisée pour empêcher la récursivité infinie par les objets auto-référençables.

Usage

J'ai créé ce service pour pouvoir disposer d'un service d'erreur accessible avec des objets. Pour qu'un service puisse enregistrer une erreur avec un objet donné et qu'un autre puisse déterminer si des erreurs ont été trouvées.

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

Cela reviendrait:

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

Tandis que

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

Cela reviendrait

[]

0

Utilisez simplement la propriété secrète cachée avec le defineProperty enumerable: false

Cela fonctionne très vite :

  • La première lecture uniqueId: 1,257,500 ops / s
  • Tous les autres: 309226485 opérations / 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
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.