Есть ли какая-либо функция хэш-кода в JavaScript?
в основном, я пытаюсь создать объект, уникальных объектов, набор. У меня была блестящая идея просто использовать объект JavaScript с объектами для имен свойств. Например,
set[obj] = true;
это работает, до определенного момента. Он отлично работает со строками и числами, но с другими объектами, все они, похоже, "хэшируют" одно и то же значение и получают доступ к одному и тому же свойству. Есть ли какой-то способ, которым я могу создать уникальное хэш-значение для объекта? Как строки и числа делают это, я могу переопределить такое же поведение?
16 ответов:
объекты JavaScript могут использовать только строки в качестве ключей (все остальное преобразуется в строку).
вы могли бы, в качестве альтернативы, поддерживать массив, который индексирует рассматриваемые объекты, и использовать его индексную строку в качестве ссылки на объект. Что-то вроде этого:
var ObjectReference = []; ObjectReference.push(obj); set['ObjectReference.' + ObjectReference.indexOf(obj)] = true;
очевидно, что это немного многословно, но вы можете написать пару методов, которые обрабатывают его и получить и установить все волей-неволей.
Edit:
ваша догадка-это факт -- это определенное поведение в JavaScript-в частности, происходит преобразование toString, что означает, что вы можете определить свою собственную функцию toString для объекта, который будет использоваться в качестве имени свойства. - Олли
это вызывает еще один интересный момент; Вы можете определить метод toString для объектов, которые вы хотите хэшировать, и это может сформировать их хэш-идентификатор.
Если вам нужна функция hashCode (), такая как Java в JavaScript, это ваше:
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; }
Это способ реализации в Java (побитовый оператор).
самый простой способ сделать это, чтобы дать каждому из объектов свое уникальное
toString
способ:(function() { var id = 0; /*global MyObject */ MyObject = function() { this.objectId = '<#MyObject:' + (id++) + '>'; this.toString= function() { return this.objectId; }; }; })();
у меня была та же проблема, и это решило ее отлично для меня с минимальной суетой, и было намного проще, что повторная реализация некоторого жирного стиля Java
Hashtable
и добавитьequals()
иhashCode()
к классам объектов. Просто убедитесь, что вы также не вставляете строку ' в свой хэш, или он уничтожит запись для вашего выхода объекта с этим идентификатор.теперь все мои хеши совершенно холодные. Я также только что опубликовал запись в блоге несколько дней назад о это точно тема.
решение, которое я выбрал, похоже на Daniel'S, но вместо того, чтобы использовать фабрику объектов и переопределять toString, я явно добавляю хэш к объекту, когда он впервые запрашивается через функцию getHashCode. Немного грязно, но лучше для моих нужд:)
Function.prototype.getHashCode = (function(id) { return function() { if (!this.hashCode) { this.hashCode = '<hash|#' + (id++) + '>'; } return this.hashCode; } }(0));
то, что вы описали, покрыто гармонией WeakMaps, часть ECMAScript 6 спецификация (следующая версия JavaScript). То есть: набор, где ключи могут быть любыми (включая неопределенные) и не перечислимы.
это означает, что невозможно получить ссылку на значение, если у вас нет прямой ссылки на ключ (любой объект!) это ссылки на него. Это важно для множества причин реализации двигателя, связанных с эффективностью и сбор мусора, но это также очень круто, поскольку он позволяет использовать новую семантику, такую как отменяемые разрешения доступа и передача данных без предоставления отправителя данных.
С 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
слабые карты доступны в текущих Firefox, Chrome и Edge. Они также поддерживаются в узле v7 и в v6 с помощью
--harmony-weak-maps
флаг.
для моей конкретной ситуации я забочусь только о равенстве объекта, насколько ключи и примитивные значения идут. Решение, которое работало для меня, преобразовывало объект в его представление JSON и использовало его в качестве хэша. Существуют ограничения, такие как порядок определения ключа, потенциально несовместимый; но, как я уже сказал, это сработало для меня, потому что все эти объекты были созданы в одном месте.
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 // }
спецификация JavaScript определяет индексированный доступ к свойствам как выполнение преобразования toString для имени индекса. Например,
myObject[myProperty] = ...;
это то же самое, что
myObject[myProperty.toString()] = ...;
это необходимо как в JavaScript
myObject["someProperty"]
это то же самое, что
myObject.someProperty
и да, мне тоже грустно: - (
Я собрал небольшой модуль JavaScript некоторое время назад для создания хэш-кодов для строк, объектов, массивов и т. д. (Я просто поручил это GitHub :))
использование:
Hashcode.value("stackoverflow") // -2559914341 Hashcode.value({ 'site' : "stackoverflow" }) // -3579752159
в ECMAScript 6 теперь есть
Set
это работает так, как вы хотели бы: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SetОн уже доступен в последних версиях Chrome, FF и IE11.
Ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
вы можете использовать символ Es6 для создания уникального ключа и объекта доступа. Каждое значение символа, возвращаемое из Symbol (), уникально. Значение символа может использоваться в качестве идентификатора свойств объекта; это единственная цель типа данных.
var obj = {}; obj[Symbol('a')] = 'a'; obj[Symbol.for('b')] = 'b'; obj['c'] = 'c'; obj.d = 'd';
вот мое простое решение, которое возвращает уникальное целое число.
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; }
Если вы действительно хотите установить поведение (я иду по знанию Java), то вам будет трудно найти решение в JavaScript. Большинство разработчиков будет рекомендовать уникальный ключ для представления каждого объекта, но это не похоже на набор, в котором вы можете получить два одинаковых объекта каждый с уникальным ключом. Java API выполняет работу по проверке повторяющихся значений путем сравнения значений хэш-кода, а не ключей, и поскольку в JavaScript нет представления значений хэш-кода объектов, он становится почти невозможно сделать то же самое. Даже прототип библиотеки JS признает этот недостаток, когда он говорит:
"хэш можно рассматривать как ассоциативный массив, связывающий уникальные ключи значения (которые не обязательно уникальный.).."
в дополнение к ответу eyelidlessness, вот функция, которая возвращает воспроизводимый, уникальный идентификатор для любого объекта:
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); }
Как вы можете видеть, он использует список для поиска, который очень неэффективен, однако это лучшее, что я мог найти на данный момент.
Если вы хотите использовать объекты в качестве ключей нужно переписать метод toString, как некоторые тут уже писали. Хэш-функции, которые были использованы, все в порядке, но они работают только для одних и тех же объектов, а не для равных объектов.
Я написал небольшую библиотеку, которая создает хэши из объектов, которые вы можете легко использовать для этой цели. Объекты могут даже иметь другой порядок, хэши будут одинаковыми. Внутренне вы можете использовать различные типы для вашего хэша (djb2, md5, sha1, sha256, sha512, ripemd160).
вот небольшой пример из документации:
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[{}]);
пакет можно использовать как в браузере, так и в Node-Js.
репозиторий:https://bitbucket.org/tehrengruber/es-js-hash
если вы хотите иметь уникальные значения в объекте поиска, вы можете сделать что-то вроде этого:
создание объекта поиска
var lookup = {};
настройка функции хэш-кода
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; }
объект
var key = getHashCode({ 1: 3, 3: 7 }); // key = '1337' lookup[key] = true;
массив
var key = getHashCode([1, 3, 3, 7]); // key = '01132337' lookup[key] = true;
прочая
var key = getHashCode('StackOverflow'); // key = 'StackOverflow' lookup[key] = true;
конечный результат
{ 1337: true, 01132337: true, StackOverflow: true }
отметим, что
getHashCode
не возвращает никакого значения, когда объект или массив пустgetHashCode([{},{},{}]); // '012' getHashCode([[],[],[]]); // '012'
это похоже на решение @ijmacd только
getHashCode
не имеетJSON
зависимость.