Есть ли какая-либо функция хэш-кода в JavaScript?

По сути, я пытаюсь создать объект из уникальных объектов, набор. У меня была блестящая идея просто использовать объект JavaScript с объектами для имен свойств. Например,

set[obj] = true;

Это работает, до определенного момента. Он отлично работает со строками и числами, но с другими объектами все они, похоже, "хэшируют" одно и то же значение и имеют доступ к одному и тому же свойству. Есть ли какой-то способ, которым я могу генерировать уникальное значение хеш-функции для объекта? Как строки и числа делают это, я могу переопределить то же самое поведение?

вопрос задан 11.10.2008
Boog
1748 репутация

16 ответов


  • 42 рейтинг

    Если вам нужна функция 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 (побитовый оператор).

    ответ дан KimKha, с репутацией 3295, 10.11.2011
  • 32 рейтинг

    Самый простой способ сделать это, дать каждому из ваших объектов свой уникальный метод toString:

    (function() {
        var id = 0;
    
        /*global MyObject */
        MyObject = function() {
            this.objectId = '
    ответ дан Daniel X Moore, с репутацией 9823, 20.05.2009
  • 31 рейтинг

    Объекты JavaScript могут использовать только строки в качестве ключей (все остальное преобразуется в строку).

    В качестве альтернативы вы можете поддерживать массив, который индексирует рассматриваемые объекты, и использовать его индексную строку в качестве ссылки на объект. Как то так:

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

    Очевидно, что это немного многословно, но вы могли бы написать пару методов, которые справятся с этим, получат и установят все волей и неволей.

    Редактировать:

    Ваше предположение является фактом - это определенное поведение в JavaScript - в частности, происходит преобразование toString, что означает, что вы можете определить свою собственную функцию toString для объекта, который будет использоваться в качестве имени свойства. - olliej

    Это поднимает еще один интересный момент; Вы можете определить метод toString для объектов, которые хотите хэшировать, и это может сформировать их хеш-идентификатор.

    ответ дан eyelidlessness, с репутацией 49251, 12.10.2008
  • 18 рейтинг

    Решение, которое я выбрал, похоже на решение Дэниела, но вместо того, чтобы использовать фабрику объектов и переопределить toString, я явно добавляю хэш к объекту, когда он сначала запрашивается через функцию getHashCode. Немного грязно, но лучше для моих нужд :)

    Function.prototype.getHashCode = (function(id) {
        return function() {
            if (!this.hashCode) {
                this.hashCode = '';
            }
            return this.hashCode;
        }
    }(0));
    
    ответ дан theGecko, с репутацией 763, 26.04.2011
  • 17 рейтинг

    То, что вы описали, покрыто Harmony 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. Они также поддерживаются в Node v7 и v6 с флагом --harmony-weak-maps.

    ответ дан KimKha, с репутацией 3295, 10.11.2011
  • 10 рейтинг

    Для моей конкретной ситуации меня интересует только равенство объекта в отношении ключей и примитивных значений. Решением, которое работало для меня, было преобразование объекта в его 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
    // }
    
    ответ дан ijmacd, с репутацией 208, 11.03.2014
  • 8 рейтинг

    Спецификация JavaScript определяет доступ к индексированным свойствам как выполнение преобразования toString для имени индекса. Например,

    myObject[myProperty] = ...;
    

    совпадает с

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

    Это необходимо как в JavaScript

    myObject["someProperty"]
    

    совпадает с

    myObject.someProperty
    

    И да, это меня тоже огорчает :-(

    ответ дан olliej, с репутацией 27505, 12.10.2008
  • 8 рейтинг

    Я недавно собрал небольшой модуль JavaScript , чтобы создавать хеш-коды для строк, объектов, массивов и т. Д. (Я только что отправил его на GitHub :))

    Использование:

    Hashcode.value("stackoverflow")
    // -2559914341
    Hashcode.value({ 'site' : "stackoverflow" })
    // -3579752159
    
    ответ дан Metalstorm, с репутацией 1172, 7.04.2013
  • 6 рейтинг

    В ECMAScript 6 теперь есть Set, который работает так, как вам нужно: https: // разработчик. Mozilla. org / en-US / docs / Web / JavaScript / Reference / Global_Objects / Set

    Он уже доступен в последних версиях Chrome, FF и IE11.

    ответ дан Daniel X Moore, с репутацией 9823, 13.01.2015
  • 3 рейтинг

    Ссылка: https: // разработчик. Mozilla. org / en-US / docs / Web / JavaScript / Справочник / Global_Objects / Symbol

    Вы можете использовать символ Es6 для создания уникального ключа и доступа к объекту. Каждое значение символа, возвращаемое из Symbol (), является уникальным. Значение символа может использоваться в качестве идентификатора для свойств объекта; это единственная цель типа данных.

    var obj = {};
    
    obj[Symbol('a')] = 'a';
    obj[Symbol.for('b')] = 'b';
    obj['c'] = 'c';
    obj.d = 'd';
    
    ответ дан Khalid Azam, с репутацией 918, 29.01.2017
  • 1 рейтинг

    Мое решение представляет статическую функцию для глобального объекта Object.

    (function() {
        var lastStorageId = 0;
    
        this.Object.hash = function(object) {
            var hash = object.__id;
    
            if (!hash)
                 hash = object.__id = lastStorageId++;
    
            return '#' + hash;
        };
    }());
    

    Я думаю, что это более удобно с другими функциями управления объектами в JavaScript.

    ответ дан Johnny, с репутацией 444, 19.02.2013
  • 1 рейтинг

    Вот мое простое решение, которое возвращает уникальное целое число.

    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;
    }
    
    ответ дан Timothy Perez, с репутацией 15432, 6.04.2017
  • 0 рейтинг

    В дополнение к ответу без век, вот функция, которая возвращает воспроизводимый, уникальный идентификатор для любого объекта:

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

    Как вы видите, он использует список для поиска, который очень неэффективен, однако это лучшее, что я мог найти на данный момент.

    ответ дан cburgmer, с репутацией 1538, 11.07.2012
  • 0 рейтинг

    Если вы хотите иметь уникальные значения в объекте поиска, вы можете сделать что-то вроде этого:

    Создание объекта поиска

    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.

    ответ дан A1rPun, с репутацией 9296, 22.10.2016
  • 0 рейтинг

    Если вы действительно хотите установить поведение (я знаю Java), вам будет сложно найти решение в JavaScript. Большинство разработчиков рекомендуют уникальный ключ для представления каждого объекта, но это не так, как установлено, так как вы можете получить два идентичных объекта каждый с уникальным ключом. Java API выполняет проверку на наличие дублирующихся значений путем сравнения значений хеш-кода, а не ключей, и, поскольку в JavaScript отсутствует представление значений хеш-кода для объектов, становится практически невозможно сделать то же самое. Даже библиотека Prototype JS признает этот недостаток, когда говорит:

    «Хеш можно представить как ассоциативный массив, привязка уникальных ключей ценностям (которые не обязательно уникальный). , , "

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

    ответ дан eyelidlessness, с репутацией 49251, 12.10.2008
  • 0 рейтинг

    Если вы хотите использовать объекты в качестве ключей, вам нужно перезаписать их метод toString, как некоторые уже упоминали здесь. Все используемые хэш-функции хороши, но они работают только для одних и тех же объектов, а не для одинаковых объектов.

    Я написал небольшую библиотеку, которая создает хэши из объектов, которые вы можете легко использовать для этой цели. Объекты могут даже иметь другой порядок, хэши будут одинаковыми. Внутренне вы можете использовать различные типы для вашего хэша (djb2, md5, sha1, sha256, sha512, palemd160).

    Вот небольшой пример из документации:

    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-J.

    Репозиторий: https: // bitbucket. org / tehrengruber / es-js-hash

    ответ дан darthmatch, с репутацией 31, 22.01.2013