18 ответов:
String.prototype.hashCode = function() { var hash = 0, i, chr; if (this.length === 0) return hash; for (i = 0; i < this.length; i++) { chr = this.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32bit integer } return hash; };источник: http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
EDIT
на основе моих тестов см. Этот тест jsperf, принято отвечать-это на самом деле быстрее: http://jsperf.com/hashcodelordvlad
оригинал
Если кто-то заинтересован, вот улучшенная (более быстрая ) версия, которая не будет работать на старых браузерах, которым не хватает
reduceфункции массива.hashCode = function(s){ return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); }
Примечание: даже с лучшим 32-битным хэшем, вам придется иметь дело с фактом это коллизии будет возникает рано или поздно. Т. е. две разные входные строки возвращает тот же хэш-значение с вероятностью не менее 1 : 2^32.
в ответе на этот вопрос какой алгоритм хэширования лучше всего подходит для уникальности и скорости?, Ян Бойд выложил хороший глубокий анализ. Короче говоря (как я это понимаю), он приходит к вывод, что ропот лучше всего, а затем FNV-1a.
Строка Java.алгоритм hashCode (), предложенный esmiralha, похоже, является вариантом DJB2.
- FNV-1a имеет лучшее распределение, чем DJB2, но медленнее
- DJB2 быстрее, чем FNV-1a, но имеет тенденцию давать больше столкновений
- MurmurHash3 лучше и быстрее, чем DJB2 и FNV-1a (но оптимизированная имплемтация требует больше строк кода, чем FNV и DJB2)
некоторые тесты с большими входными строками здесь:http://jsperf.com/32-bit-hash
Когда короче входные строки хэшируются, производительность murmur падает относительно DJ2B и FNV-1a:http://jsperf.com/32-bit-hash/3Так что в целом я бы рекомендовал murmur3.
Смотрите здесь для JavaScript реализация: https://github.com/garycourt/murmurhash-jsесли входные строки короткие и производительность важнее качества распространения, используйте DJB2 (как предложено в принятом ответе esmiralha).
если качество и малый размер кода важнее скорости, я использую эту реализацию FNV-1a (на основе код).
/** * Calculate a 32 bit FNV-1a hash * Found here: https://gist.github.com/vaiorabbit/5657561 * Ref.: http://isthe.com/chongo/tech/comp/fnv/ * * @param {string} str the input value * @param {boolean} [asString=false] set to true to return the hash value as * 8-digit hex string instead of an integer * @param {integer} [seed] optionally pass the hash of the previous chunk * @returns {integer | string} */ function hashFnv32a(str, asString, seed) { /*jshint bitwise:false */ var i, l, hval = (seed === undefined) ? 0x811c9dc5 : seed; for (i = 0, l = str.length; i < l; i++) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } if( asString ){ // Convert to 8 digit hex string return ("0000000" + (hval >>> 0).toString(16)).substr(-8); } return hval >>> 0; }
на основе принято отвечать в ES6. Меньше, ремонтопригоден и работает в современных браузерах.
function hashCode(str) { return str.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0); } // Test console.log("hashCode(\"Hello!\"): ", hashCode('Hello!'));
Если это кому-то поможет, я объединил два верхних ответа в более старую версию браузера, которая использует быструю версию if
reduceдоступно и возвращается к решению esmiralha, если это не так./** * @see http://stackoverflow.com/q/7616461/940217 * @return {number} */ String.prototype.hashCode = function(){ if (Array.prototype.reduce){ return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); } 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; }использование как:
var hash = new String("some string to be hashed").hashCode();
Это изысканный и более эффективный вариант:
String.prototype.hashCode = function() { var hash = 0, i = 0, len = this.length; while ( i < len ) { hash = ((hash << 5) - hash + this.charCodeAt(i++)) << 0; } return hash; };это соответствует реализации Java стандарта
object.hashCode()вот также один, который возвращает только положительные хэш-коды:
String.prototype.hashcode = function() { return (this.hashCode() + 2147483647) + 1; };и вот это для Java, который возвращает только положительные хэш-кодов:
public static long hashcode(Object obj) { return ((long) obj.hashCode()) + Integer.MAX_VALUE + 1l; }наслаждайтесь!
Я немного удивлена, что никто не говорил о новом SubtleCrypto API еще.
чтобы получить хэш из строки, вы можете использовать
subtle.digestспособ :function getHash(str, algo = "SHA-256") { let strBuf = new TextEncoder('utf-8').encode(str); return crypto.subtle.digest(algo, strBuf) .then(hash => { window.hash = hash; // here hash is an arrayBuffer, // so we'll connvert it to its hex version let result = ''; const view = new DataView(hash); for (let i = 0; i < hash.byteLength; i += 4) { result += ('00000000' + view.getUint32(i).toString(16)).slice(-8); } return result; }); } getHash('hello world') .then(hash => { console.log(hash); });
благодаря примеру mar10, я нашел способ получить тот же результат в C# и JavaScript для ФПН-1А. Если Юникод символы присутствуют, верхняя часть отбрасывается ради производительности. Не знаю, почему было бы полезно поддерживать их при хэшировании, так как сейчас я только хэширую url-пути.
Версия C#
private static readonly UInt32 FNV_OFFSET_32 = 0x811c9dc5; // 2166136261 private static readonly UInt32 FNV_PRIME_32 = 0x1000193; // 16777619 // Unsigned 32bit integer FNV-1a public static UInt32 HashFnv32u(this string s) { // byte[] arr = Encoding.UTF8.GetBytes(s); // 8 bit expanded unicode array char[] arr = s.ToCharArray(); // 16 bit unicode is native .net UInt32 hash = FNV_OFFSET_32; for (var i = 0; i < s.Length; i++) { // Strips unicode bits, only the lower 8 bits of the values are used hash = hash ^ unchecked((byte)(arr[i] & 0xFF)); hash = hash * FNV_PRIME_32; } return hash; } // Signed hash for storing in SQL Server public static Int32 HashFnv32s(this string s) { return unchecked((int)s.HashFnv32u()); }Версия JavaScript
var utils = utils || {}; utils.FNV_OFFSET_32 = 0x811c9dc5; utils.hashFnv32a = function (input) { var hval = utils.FNV_OFFSET_32; // Strips unicode bits, only the lower 8 bits of the values are used for (var i = 0; i < input.length; i++) { hval = hval ^ (input.charCodeAt(i) & 0xFF); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } return hval >>> 0; } utils.toHex = function (val) { return ("0000000" + (val >>> 0).toString(16)).substr(-8); }
мне нужна была аналогичная функция (но другая) для создания уникального идентификатора на основе имени пользователя и текущего времени. Итак:
window.newId = -> # create a number based on the username unless window.userNumber? window.userNumber = 0 for c,i in window.MyNamespace.userName char = window.MyNamespace.userName.charCodeAt(i) window.MyNamespace.userNumber+=char ((window.MyNamespace.userNumber + Math.floor(Math.random() * 1e15) + new Date().getMilliseconds()).toString(36)).toUpperCase()выдает:
2DVFXJGEKL 6IZPAKFQFL ORGOENVMG ... etcредактировать июня 2015 года: в новом коде я использую shortid: https://www.npmjs.com/package/shortid
быстрый и краткий, который был адаптирован из здесь:
String.prototype.hashCode = function() { var hash = 5381, i = this.length while(i) hash = (hash * 33) ^ this.charCodeAt(--i) return hash >>> 0; }
мой быстрый (очень длинный) один лайнер на основе FNV
Multiply+Xorспособ:my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16);
Если вы хотите избежать столкновений, вы можете использовать безопасная хэш - как SHA-256. Существует несколько реализаций JavaScript SHA-256.
Я написал тесты для сравнения нескольких хэш-реализаций, см. https://github.com/brillout/test-javascript-hash-implementations.
или перейти к http://brillout.github.io/test-javascript-hash-implementations/, чтобы выполнить тесты.
Я объединил два решения (пользователи esmiralha и lordvlad), чтобы получить функцию, которая должна быть быстрее для браузеров, поддерживающих функцию js уменьшить() и все еще совместима со старыми браузерами:
String.prototype.hashCode = function() { if (Array.prototype.reduce) { return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0); } else { var hash = 0, i, chr, len; if (this.length == 0) return hash; for (i = 0, len = this.length; i < len; i++) { chr = this.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32bit integer } return hash; } };пример:
my_string = 'xyz'; my_string.hashCode();
Я пошел на простую конкатенацию кодов символов, преобразованных в шестнадцатеричные строки. Это служит относительно узкой цели, а именно просто нужно хэш-представление короткой строки (например, заголовки, теги) для обмена с серверной стороной, которая по не соответствующим причинам не может легко реализовать принятый порт Java хэш-кода. Очевидно, что здесь нет приложения безопасности.
String.prototype.hash = function() { var self = this, range = Array(this.length); for(var i = 0; i < this.length; i++) { range[i] = i; } return Array.prototype.map.call(range, function(i) { return self.charCodeAt(i).toString(16); }).join(''); }Это может быть сделано более лаконичным и браузерным с подчеркиванием. Пример:
"Lorem Ipsum".hash() "4c6f72656d20497073756d"I предположим, что если вы хотите хэшировать большие строки аналогичным образом, вы можете просто уменьшить коды символов и шестнадцатерить полученную сумму, а не объединять отдельные символы вместе:
String.prototype.hashLarge = function() { var self = this, range = Array(this.length); for(var i = 0; i < this.length; i++) { range[i] = i; } return Array.prototype.reduce.call(range, function(sum, i) { return sum + self.charCodeAt(i); }, 0).toString(16); } 'One time, I hired a monkey to take notes for me in class. I would just sit back with my mind completely blank while the monkey scribbled on little pieces of paper. At the end of the week, the teacher said, "Class, I want you to write a paper using your notes." So I wrote a paper that said, "Hello! My name is Bingo! I like to climb on things! Can I have a banana? Eek, eek!" I got an F. When I told my mom about it, she said, "I told you, never trust a monkey!"'.hashLarge() "9ce7"естественно больше риска столкновения с этим методом, хотя вы могли бы возиться с арифметикой в уменьшении, однако вы хотели диверсифицировать и удлинить хэш.
немного упрощенная версия ответа @esmiralha.
Я не переопределяю строку в этой версии, так как это может привести к некоторому нежелательному поведению.
function hashCode(str) { var hash = 0; for (var i = 0; i < str.length; i++) { hash = ~~(((hash << 5) - hash) + str.charCodeAt(i)); } return hash; }
вот простой, хорошо распределенный 53-битный хэш. Это довольно быстро и имеет низкую скорость столкновения.
var cyrb53 = function(str) { var p1 = 2654435761, p2 = 1597334677, h1 = 0xdeadbeef | 0, h2 = 0x41c6ce57 | 0; for (var i = 0; i < str.length; i++) ch = str.charCodeAt(i), h1 = Math.imul(h1 + ch, p1), h2 = Math.imul(h2 + ch, p2); h1 = Math.imul(h1 ^ h1 >>> 16, p2), h2 = Math.imul(h2 ^ h2 >>> 15, p1); return (h2 & 2097151) * 4294967296 + h1; };он достигает лавины (нестрогой), поэтому небольшие изменения имеют большие изменения в выходе, что делает его случайным:
0xe00c44e568f86 = "a" 0x893099dbedf04 = "b" 0x98f3f59367810 = "revenge" 0x45f880d099bbf = "revenue"53-бит является пределом целых чисел JS и имеет значительно меньшую вероятность столкновения, чем 32-битные хэши. Но если 53 бита недостаточно для вас, вы все равно можете использовать все 64 бита, построив шестнадцатеричную строку или массив:
return (h2>>>0).toString(16).padStart(8,0)+(h1>>>0).toString(16).padStart(8,0); // or return [h2>>>0, h1>>>0];загвоздка в том, что построение шестнадцатеричной строки становится узким местом по производительности, и массиву нужны два оператора сравнения вместо одного, что не так удобно.
С помощью этого решения мы можем указать набор символов, чтобы избежать некоторых проблем, когда значения хранятся или отправляются между слоями приложений, например: когда результирующая строка (хэш) производит процентное кодирование, и эта строка отправляется контроллеру с помощью метода GET из слоя представления.
function doHashCode() { String.prototype.hashCode = function () { var text = ""; var possible = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; for (var i = 0; i < 15; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; } var hash = new String().hashCode(); $('#input-text-hash').val(hash); // your html input text }
Я немного опоздала на вечеринку, но вы можете использовать этот модуль: крипто:
const crypto = require('crypto'); const SALT = '$ome$alt'; function generateHash(pass) { return crypto.createHmac('sha256', SALT) .update(pass) .digest('hex'); }результатом этой функции всегда является
64строка символов; что-то вроде этого:"aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"
Comments