Existe-t-il un type de fonction de code hash en JavaScript?

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

set[obj] = true; 

Cela fonctionne, jusqu'à un certain point. Il fonctionne très bien avec des chaînes et des chiffres, mais avec d'autres objets, ils semblent tous "hash" à 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-ils, puis-je remplacer le même comportement?

Marcodidence illustrdd illustrdd illustr portfólioAdéldiceAachemetachachistándeterádiceaddeterdeterddáddádeterdeterelaséinahashashashasenasdMachelasagensé aleeavasdeterddáddádádeterédáddádádádééAádádádádádÉádddádádÉAFé Confiladas aledélvesdéliceún aled

Vous pouvez, par conséquent, maintenir 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; 

Évidemment, c'est un petit détail, mais vous pourriez écrire quelques méthodes qui le traitent et obtenez et mettez tous les willy nilly.

Modifier:

Votre estimation est un fait – ce comportement est défini en JavaScript – en particulier, une conversion toString 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; Dddidence Daeaéleaeaeaeaeaeaeaeaeaeaeaeaeaidenceidence Marco illustridence Marco Dafeea Da Marco citadine Trause Daea Da Marco Dafe Da Marco citadine

Si vous voulez une fonction hashCode () comme Java en JavaScript, c'est à vous:

 String.prototype.hashCode = function(){ var hash = 0; if (this.length == 0) return hash; 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 la manière d'implémenter Java (opérateur bitwise).

La façon la plus simple de le faire est de donner à chacun de vos objets sa propre méthode toString 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 m'a résolu parfaitement pour moi avec un minimum d'agitation, et il était beaucoup plus facile de ré-implémenter un Hashtable style Java gras et d'ajouter equals() et hashCode() à vos classes d'objets. Assurez-vous simplement que vous ne collez pas non plus une chaîne '<#MyObject: 12> dans votre hachage ou il effacera l'entrée pour votre objet sortant avec cet identifiant.

Maintenant, tous mes hachis sont totalement froids. J'ai également posté une entrée sur le blog il y a quelques jours à propos de ce sujet précis .

La solution que j'ai choisie est similaire à celle de Daniel, mais plutôt que d'utiliser une usine d'objets et de remplacer toString, j'ajoute explicitement le hash à l'objet lorsqu'il est demandé pour la première fois grâce à une fonction getHashCode. Un peu désordonné, mais mieux pour mes besoins 🙂

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

Ce que vous avez décrit est couvert par Harmony WeakMaps , une 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 n'est pas énumérable.

Cela signifie qu'il est impossible d'obtenir une référence à une valeur, sauf si vous avez une référence directe à la clé (n'importe quel objet!) Qui s'y rattache. Il est important pour un tas de raisons de mise en œuvre du moteur liées à l'efficacité et à la collecte des ordures, mais il est également super cool car il permet de nouvelles sémantiques comme les autorisations d'accès rémanente et le passage des 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 actuel. Ils sont également pris en charge dans Node v7 et en v6 avec l' --harmony-weak-maps .

J'ai mis en place un petit module JavaScript il y a quelque temps pour produire des codes de hash pour les chaînes, les objets, les tableaux, etc. (je l'ai simplement confié à GitHub :))

Usage:

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

Pour ma situation spécifique, je me soucie seulement de l'égalité de l'objet en ce qui concerne les clés et les valeurs primitives. La solution qui a fonctionné pour moi était la conversion de l'objet en sa représentation JSON et l'utilisation de ce hash. Il existe des limites telles que l'ordre de la définition de clé 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 en un seul 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 // } 

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

 myObject[myProperty] = ...; 

est le même que

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

Ceci est nécessaire comme dans JavaScript

 myObject["someProperty"] 

est le même que

 myObject.someProperty 

Et oui, cela me rend aussi triste 🙁

Dans ECMAScript 6, il existe maintenant un Set qui 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.

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 accéder à un objet. Chaque symbole renvoyé par Symbol () est unique. Une valeur de symbole peut être utilisée comme identifiant pour les propriétés de l'objet; C'est le seul but du type de données.

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

Ma solution introduit une fonction statique pour l' Object objet 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 cela est plus pratique avec d'autres fonctions de manipulation d'objets en JavaScript.

Si vous voulez vraiment un comportement défini (je vais en Java), vous serez difficile de trouver une solution en JavaScript. La plupart des développeurs recommandent une clé unique pour représenter chaque objet, mais ce n'est pas différent, car 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 hash, pas les clés, et comme il n'y a pas de représentation de valeurs de code hash en JavaScript, il devient presque impossible de faire de même. Même la bibliothèque Prototype JS admet cette lacune, quand elle dit:

"Hash peut être considéré comme un tableau associatif, reliant des clés uniques aux valeurs (qui ne sont pas nécessairement uniques) …"

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

En plus de la réponse de la paupière, voici une fonction qui renvoie une ID unique et reproductible pour n'importe quel objet:

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

Comme vous pouvez le voir, vous utilisez une liste de recherche qui est très inefficace, mais c'est le meilleur que j'ai pu trouver pour l'instant.

Si vous souhaitez utiliser des objets en tant que clés, vous devez écraser leur méthode toString, comme certains l'ont déjà mentionné. Les fonctions de hachage qui ont été utilisées sont toutes bien, 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 hash (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 paquet peut être utilisé dans le navigateur et dans Node-Js.

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

Si vous souhaitez 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 code haché

 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; 

Array

 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 getHashCode ne renvoie aucune valeur lorsque l'objet ou le tableau est vide

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

Ceci est similaire à la solution @ijmacd seulement getHashCode n'a pas la dépendance JSON .

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