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

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

set[obj] = true;

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

Ответ 1

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

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

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

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

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

Edit:

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

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

Ответ 2

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

Ответ 3

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

(function() {
    var id = 0;

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

У меня была такая же проблема, и это решило это для меня с минимальной суетой, и было намного проще повторить реализацию жирного стиля Java Hashtable и добавить equals() и hashCode() в классы объектов. Просто убедитесь, что вы также не вставляете строку '< #MyObject: 12 > в свой хеш, иначе она уничтожит запись для вашего выходящего объекта с этим идентификатором.

Теперь все мои хэши полностью озноб. Я также опубликовал запись в блоге несколько дней назад о этой точной теме.

Ответ 4

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

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

Ответ 5

На то, что вы описали, распространяется 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.

Ответ 6

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

Ответ 7

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

myObject[myProperty] = ...;

совпадает с

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

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

myObject["someProperty"]

совпадает с

myObject.someProperty

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

Ответ 8

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

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

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

Ответ 10

Ссылка: 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';

Ответ 11

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

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

Ответ 12

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

(function() {
    var lastStorageId = 0;

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

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

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

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

Ответ 13

Я постараюсь пойти немного глубже, чем другие ответы.

Даже если бы у JS была лучшая поддержка хеширования, он не мог бы магически все хешировать, во многих случаях вам придется определять свою собственную хеш-функцию. Например, в Java есть хорошая поддержка хеширования, но вам все еще нужно подумать и поработать.

Одна проблема связана с термином хэш/хэш-код... существует криптографическое хеширование и некриптографическое хеширование. Другая проблема заключается в том, что вы должны понимать, почему хеширование полезно и как оно работает.

Когда мы говорим о хешировании в JavaScript или Java, большую часть времени мы говорим о некриптографическом хешировании, обычно о хешировании для hashmap/hashtable (если мы не работаем над аутентификацией или паролями, которые вы могли бы выполнять на стороне сервера, используя NodeJS...).

Это зависит от того, какие данные у вас есть и чего вы хотите достичь.

Ваши данные имеют некоторую естественную "простую" уникальность:

  • Хэш целого числа - это... целое число, поскольку оно уникально, удачи вам!
  • Хеш строки... это зависит от строки, если строка представляет уникальный идентификатор, вы можете рассматривать его как хеш (поэтому хеширование не требуется).
  • Все, что косвенно является уникальным целым числом, является простейшим случаем
  • Это будет уважать: хэш-код равен, если объекты равны

Ваши данные имеют некоторую естественную "составную" уникальность:

  • Например, для объекта person вы можете вычислить хеш, используя имя, фамилию, дату рождения,... посмотрите, как это делает Java: Хорошая функция хеширования для строк, или используйте некоторую другую информацию идентификатора, которая дешева и достаточно уникален для вашего варианта использования

Вы не представляете, какими будут ваши данные:

  • Удачи... вы можете сериализовать строку и хэшировать ее в стиле Java, но это может быть дорого, если строка большая, и это не позволит избежать коллизий, а также скажет хэш целого числа (self).

Не существует магически эффективного метода хеширования неизвестных данных, в некоторых случаях это довольно легко, в других случаях вам, возможно, придется подумать дважды. Таким образом, даже если JavaScript/ECMAScript добавляет дополнительную поддержку, для этой проблемы не существует решения на волшебном языке.

На практике вам нужны две вещи: достаточно уникальности, достаточно скорости

В дополнение к этому здорово иметь: "хэш-код равен, если объекты равны"

Ответ 14

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

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

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

Ответ 15

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

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

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

Ответ 16

Если вы хотите использовать объекты в качестве ключей, вам необходимо перезаписать их метод 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

Ответ 17

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

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

var lookup = {};

Настройка функции 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;
}

Объект

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.

Ответ 18

Я объединил ответы от отсутствия век и КимХа.

Ниже приведен сервис angularjs, который поддерживает числа, строки и объекты.

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

Пример использования:

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

Выход

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

Объяснение

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

Сравнение объектов без век используется для предотвращения бесконечной рекурсии путем самообращения объектов.

ИспользованиеЯ создал этот сервис, чтобы у меня мог быть сервис ошибок, к которому обращаются объекты. Чтобы одна служба могла зарегистрировать ошибку с данным объектом, а другая - определить, были ли обнаружены какие-либо ошибки.

т.е.

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

Это вернется:

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

Хотя

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

Это вернет

[]

Ответ 19

Основываясь на заголовке, мы можем генерировать сильные хэши с помощью js, его можно использовать для генерации уникального хеша из объекта, массива параметров, строки или чего-либо еще.

Позже для индексации это позволит избежать любых возможных ошибок сопоставления, позволяя при этом извлекать индекс из параметров (избегайте поиска/зацикливания объекта и т.д.):

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