991

I need to convert strings to some form of hash. Is this possible in JavaScript?

I'm not utilizing a server-side language so I can't do it that way.

20
  • 10
    MD5 is not secure, so don't look for that one.CommentedAug 20, 2013 at 20:30
  • 293
    @henrikstroem Depends on what you are hashing; there's nothing wrong with using md5 to make a hash for non-security purposes.
    – Brad Koch
    CommentedJun 26, 2014 at 17:18
  • 16
    @BradKoch Depends on what you are doing; there's nothing wrong for using md5 for security purposes. There are certainly better methods for hashing passwords, but md5 is just fine for doing things like signing a URL.CommentedMar 16, 2015 at 7:05
  • 131
    I find it funny that while MD5 is criticised in comments here, almost all answers recommend much worse hash algorithms and get lots of upvotes.
    – domen
    CommentedJan 28, 2016 at 14:35
  • 102
    Using MD5 to verify that a download came intact is not magically going to email your passwords to all your co-workers.CommentedJan 6, 2018 at 1:28

29 Answers 29

1076

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; } const str = 'revenue' console.log(str, str.hashCode())

Source

47
  • 46
    This is the same one used in Java. The hash << 5 - hash is the same as hash * 31 + char but a LOT faster. It's nice because it's so fast, and 31 is a small prime. Win win there.
    – corsiKa
    CommentedSep 30, 2011 at 21:59
  • 19
    @PeterAronZentai Why is it "unusable"? The output produced by the number-based code (hash * 31) + char is identical to the output produced by the shift-based code ((hash<<5)-hash)+char, even for very long strings (I've tested it with strings containing over a million characters), so it's not "unusable" in terms of accuracy. The complexity is O(n) for both the number-based and shift-based versions, so it's not "unusable" in terms of complexity.CommentedJul 19, 2013 at 1:53
  • 29
    Can anyone comment on the uniqueness (or not) of the output? Specifically, if I only use this hash for strings with length less than n, what is the largest n for which I can't possibly have a collision?CommentedMay 23, 2014 at 5:26
  • 66
    Is there any reason this needs (or should) be on the String prototype? Would it be any less effective/efficient to just have eg; var hashCode = function hashCode (str) {etc...}? And then use as hashCode("mystring")?
    – rattray
    CommentedAug 4, 2014 at 14:24
  • 12
    I wonder if removing the line if (this.length == 0) return hash; will have a major impact on performance. In terms of clean code, in my opinion, it is just noise.
    – user40171
    CommentedJul 6, 2015 at 8:55
464

Many of the answers here are the same String.hashCode hash function taken from Java. It dates back to 1981 from Gosling Emacs, is extremely weak, and makes zero sense performance-wise in modern JavaScript. In fact, implementations could be significantly faster by using ES6 Math.imul, but no one took notice. We can do much better than this, at essentially identical performance.

Here's one I did—cyrb53, a simple but high quality 53-bit hash. It's quite fast, provides very good* hash distribution, and because it outputs 53 bits, has significantly lower collision rates compared to any 32-bit hash. Also, you can ignore SA's CC license as it's public domain on my GitHub.

const cyrb53 = (str, seed = 0) => { let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; for(let i = 0, ch; i < str.length; i++) { ch = str.charCodeAt(i); h1 = Math.imul(h1 ^ ch, 2654435761); h2 = Math.imul(h2 ^ ch, 1597334677); } h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507); h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909); h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507); h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); return 4294967296 * (2097151 & h2) + (h1 >>> 0); }; console.log(`cyrb53('a') -> ${cyrb53('a')}`) console.log(`cyrb53('b') -> ${cyrb53('b')}`) console.log(`cyrb53('revenge') -> ${cyrb53('revenge')}`) console.log(`cyrb53('revenue') -> ${cyrb53('revenue')}`) console.log(`cyrb53('revenue', 1) -> ${cyrb53('revenue', 1)}`) console.log(`cyrb53('revenue', 2) -> ${cyrb53('revenue', 2)}`) console.log(`cyrb53('revenue', 3) -> ${cyrb53('revenue', 3)}`)

*It is roughly similar to the well-known MurmurHash/xxHash algorithms. It uses a combination of multiplication and Xorshift to generate the hash, but not as thorough. As a result it's significantly simpler to implement, but may not pass all tests in SMHasher. This is not a cryptographic hash function, so don't use this for security purposes.

Like any proper hash, it has a fairly acceptable "avalanche" effect, which basically means small changes in the input have big changes in the output, making the resulting hash appear more 'random':

"501c2ba782c97901" = cyrb53("a") "459eda5bc254d2bf" = cyrb53("b") "fbce64cc3b748385" = cyrb53("revenge") "fb1d85148d13f93a" = cyrb53("revenue") 

You can optionally supply a seed (unsigned integer, 32-bit max) for alternate streams of the same input:

"76fee5e6598ccd5c" = cyrb53("revenue", 1) "1f672e2831253862" = cyrb53("revenue", 2) "2b10de31708e6ab7" = cyrb53("revenue", 3) 

Technically, it is a 64-bit hash, that is, two uncorrelated 32-bit hashes computed in parallel, but JavaScript is limited to 53-bit integers. If convenient, the full 64-bit output can be used by altering the return statement with a hex string or array.

return [h2>>>0, h1>>>0]; // or return (h2>>>0).toString(16).padStart(8,0)+(h1>>>0).toString(16).padStart(8,0); // or return 4294967296n * BigInt(h2) + BigInt(h1); 

Be aware that constructing hex strings drastically slows down batch processing. The array is much more efficient, but obviously requires two checks instead of one. I also included BigInt, which should be slightly faster than String, but still much slower than Array or Number.


Just for fun, here's TinySimpleHash, the smallest hash I could come up with that's still decent. It's a 32-bit hash in 89 chars with better quality randomness than even FNV or DJB2:

TSH=s=>{for(var i=0,h=9;i<s.length;)h=Math.imul(h^s.charCodeAt(i++),9**9);return h^h>>>9} 
40
  • 14
    Wow, this is so much better than the usual *31 one for short (or similar) inputs. :)
    – lapo
    CommentedOct 3, 2018 at 7:59
  • 5
    @BachT You can use a polyfill or a full ES6 shim. But IE11 is tragically frozen in 2009 with no updates.
    – bryc
    CommentedMar 19, 2019 at 5:48
  • 4
    @bryc How do you pick those big numbers, which aren't necessarily prime? How do they effect the efficiency and the performance of the result?CommentedOct 9, 2019 at 9:35
  • 9
    @KossiD.T.S. I didn't pick those multipliers; they were borrowed elsewhere (L'Ecuyer, Knuth, MurmurHash). Usually these numbers are found by clever people via probabilistic testing (e.g. simulated annealing, genetic programming), searching for the best statistical result, tuned for their use case. They don't really affect efficiency, just quality of the hash result. For example, making them even numbers typically breaks everything, but millions of combinations of odd numbers can give decent results. I'm sure even better constants could be found. I never tested this with SMHasher.
    – bryc
    CommentedOct 9, 2019 at 22:39
  • 4
    @aomarks sorry, i don't vibe with middle age royal decrees; it's public domain, take that as you will. maybe if MIT offered me free tuition to endorse them ;)
    – bryc
    CommentedNov 18, 2020 at 21:58
201

EDIT

based on my jsperf tests, the accepted answer is actually faster: http://jsperf.app/hashcodelordvlad

ORIGINAL

if anyone is interested, here is an improved ( faster ) version, which will fail on older browsers who lack the reduce array function.

hashCode = function(s) { return s.split("").reduce(function(a, b) { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0); } // testing console.log(hashCode("hello.")); console.log(hashCode("this is a text.")); console.log(hashCode("Despacito by Luis Fonsi"));

one-liner arrow function version :

hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0) // testing console.log(hashCode("hello.")); console.log(hashCode("this is a text.")); console.log(hashCode("Despacito by Luis Fonsi"));

24
  • 9
    is there a way to get hash wich is positive number only?CommentedNov 8, 2013 at 15:26
  • 74
    weird. i just tested it and it turned out to be waaay slower than the accepted answer. jsperf.com/hashcodelordvlad
    – lordvlad
    CommentedDec 18, 2013 at 16:34
  • 204
    Good guy @lordvlad, actually testing his own answer, and then reporting when it was slower.CommentedFeb 24, 2014 at 13:32
  • 14
    I just realized: It makes perfect sense that the accepted answer is faster, because my version has to turn the string into an array first, allocating new memory and copying every character...
    – lordvlad
    CommentedFeb 22, 2015 at 10:04
  • 8
    [].reduce.call(str, (p, c, i, a) => (p << 5) - p + a.charCodeAt(i), 0);
    – Dizzy
    CommentedMar 24, 2016 at 12:46
142

Note: Even with the best 32-bit hash, collisions will occur sooner or later.

The hash collision probability can be calculated as 1 - e ^ (-k(k-1) / 2N, approximated as k^2 / 2N (see here). This may be higher than intuition suggests:
Assuming a 32-bit hash and k=10,000 items, a collision will occur with a probability of 1.2%. For 77,163 samples the probability becomes 50%! (calculator).
I suggest a workaround at the bottom.

In an answer to this question Which hashing algorithm is best for uniqueness and speed?, Ian Boyd posted a good in depth analysis. In short (as I interpret it), he comes to the conclusion that MurmurHash is best, followed by FNV-1a.
Java’s String.hashCode() algorithm that esmiralha proposed seems to be a variant of DJB2.

  • FNV-1a has a a better distribution than DJB2, but is slower
  • DJB2 is faster than FNV-1a, but tends to yield more collisions
  • MurmurHash3 is better and faster than DJB2 and FNV-1a (but the optimized implementation requires more lines of code than FNV and DJB2)

Some benchmarks with large input strings here: http://jsperf.app/32-bit-hash
When short input strings are hashed, murmur's performance drops, relative to DJ2B and FNV-1a: http://jsperf.app/32-bit-hash/3

So in general I would recommend murmur3.
See here for a JavaScript implementation: https://github.com/garycourt/murmurhash-js

If input strings are short and performance is more important than distribution quality, use DJB2 (as proposed by the accepted answer by esmiralha).

If quality and small code size are more important than speed, I use this implementation of FNV-1a (based on this code).

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

Improve Collision Probability

As explained here, we can extend the hash bit size using this trick:

function hash64(str) { var h1 = hash32(str); // returns 32 bit (as 8 byte hex string) return h1 + hash32(h1 + str); // 64 bit (as 16 byte hex string) } 

Use it with care and don't expect too much though.

11
  • Why do you do ("0000000" + (hval >>> 0).toString(16)).substr(-8);? Isn't that the same as (hval >>> 0).toString(16)?CommentedOct 22, 2014 at 14:14
  • 4
    this add leading '0's so that the resulting hash is always 8 chars long. Easier to read and recognize in outputs, but that's my personal opinion
    – mar10
    CommentedOct 22, 2014 at 14:53
  • Ah ok, I get it. For small hval, (hval >>> 0).toString(16) might be less than 8 characters, so you pad it with zeros. I was just confused because (hval >>> 0).toString(16) always resulted in a exactly 8 character string for me.CommentedOct 22, 2014 at 15:02
  • 3
    I love this answer because it produces a sooo much better distributed hash: other functions proposed here will make consequent hash values. Eg `hash("example1") - hash("example2") == 1", while this one is much more unpredictable.CommentedOct 7, 2016 at 6:26
  • 2
    In response to "FNV-1a has a a better distribution than DJB2, but is slower" - I think it should be said that FNV1a can be extremely fast when implemented using the ES6 Math.imul function. That alone makes it top benchmarks, and ultimately a better choice than DJB2 in the long run.
    – bryc
    CommentedJan 10, 2019 at 0:53
103

Based on accepted answer in ES6. Smaller, maintainable and works in modern browsers.

function hashCode(str) { return str.split('').reduce((prevHash, currVal) => (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0); } // Test console.log("hashCode(\"Hello!\"): ", hashCode('Hello!'));

EDIT (2019-11-04):

one-liner arrow function version :

const hashCode = s => s.split('').reduce((a,b) => (((a << 5) - a) + b.charCodeAt(0))|0, 0) // test console.log(hashCode('Hello!'))

14
  • 1
    Thanks for sharing I added str += "" before hashing to avoid exception str.split is not a function thrown when non-strings were passed as parametersCommentedJul 11, 2016 at 10:17
  • 8
    But much, much slower than any of these: https://jsperf.com/hashing-strings
    – AndyO
    CommentedApr 27, 2017 at 9:34
  • 4
    Any way to have this produce only positive but still unique results?
    – Dids
    CommentedNov 6, 2017 at 13:01
  • 3
    @BeetleJuice The more appropriate question is if you have a function that is designed to take a string then why is your program sending it a non-string in the first place? Perhaps the error is a sign that the caller is doing strange things. Food for thought.
    – Sukima
    CommentedMar 29, 2018 at 13:25
  • 4
    @deekshith The accepted answer uses hash |= 0 to convert to an 32 bit int. This implementation does not. Is this a bug?
    – Sukima
    CommentedMar 29, 2018 at 14:35
68

I'm a bit surprised nobody has talked about the new SubtleCrypto API yet.

To get an hash from a string, you can use the subtle.digest method :

function getHash(str, algo = "SHA-256") { let strBuf = new TextEncoder().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); });

7
  • 5
    I agree. The conversion to hex could be done a little different... var promise = crypto.subtle.digest({name: "SHA-256"}, Uint8Array.from(data)); promise.then(function(result){ console.log(Array.prototype.map.call(new Uint8Array(result), x => x.toString(16).padStart(2, '0')).join('')); });CommentedOct 3, 2017 at 15:01
  • 8
    A cryptographic hash function for strings is a bit overkill.. crypto isn't exactly performant.
    – bryc
    CommentedOct 3, 2018 at 22:43
  • 1
    Reliable quality random without having to rely on people running tests, built-in (no custom implementation needed), seedable, and I only needed a few hundred numbers for generating a game map, this seemed perfect. But it turns out there is absolutely no way to do it synchronously. Having to provide some async callback thingy every time you call your seeded random engine makes the code super unreadable and look ridiculous. I don't get who came up with this crappy crypto.subtle interface, so I in the end I had to go with xmur3+sfc32 from this answer: stackoverflow.com/a/47593316/1201863
    – Luc
    CommentedMay 1, 2020 at 17:49
  • I'm confused about the line result += ('00000000' + view.getUint32(i).toString(16)).slice(-8); What is the point of adding 8 zeroes 00000000 and then slicing them off at the end? .slice(-8)? When I remove those parts I get the exact same result (in my very limited amount of testing). What am I missing?
    – I0_ol
    CommentedMay 7, 2022 at 8:07
  • 1
    @I0_ol because you always got a value higher than 0xFF0000 Simply try with view = new DataView(new Uint32Array([0]).buffer). You'll get "0" without the padding and "00000000" with.
    – Kaiido
    CommentedMay 7, 2022 at 13:38
43

This is a refined and better performing variant, and matchesJava's implementation of the standard object.hashCode() for CharSequence.

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

Here is also one that returns only positive hashcodes:

String.prototype.hashcode = function() { return this.hashCode()+ 2147483647 + 1; }; 

And here is a matching one for Java that only returns positive hashcodes:

public static long hashcode(Object obj) { return ((long) obj.hashCode()) + Integer.MAX_VALUE + 1l; } 

Without prototype for those that do not want to attach it to String :

function hashCode(str) { var hash = 0, i = 0, len = str.length; while ( i < len ) { hash = ((hash << 5) - hash + str.charCodeAt(i++)) << 0; } return hash; } function hashcode(str) { return hashCode(str) + 2147483647 + 1; } 

Enjoy!

16
  • 2
    great answer, but what is the purpose of << 0 ?
    – koolaang
    CommentedDec 18, 2016 at 16:58
  • 12
    @koolaang it's the left shit operator, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    – mjs
    CommentedDec 20, 2016 at 9:27
  • 59
    @momomo Did you mean left shift?
    – wdh
    CommentedJan 25, 2017 at 11:15
  • 5
    @momomo I think he was asking why it was a left shift of zero bits.
    – jpfx1342
    CommentedMay 5, 2017 at 1:29
  • 3
    @Maykonn (2^32 - 1)CommentedDec 19, 2018 at 13:25
30

If it helps anyone, I combined the top two answers into an older-browser-tolerant version, which uses the fast version if reduce is available and falls back to esmiralha's solution if it's not.

/** * @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; } 

Usage is like:

var hash = "some string to be hashed".hashCode(); 
3
  • how to optimize this code to run faster in every browser. String.prototype.hashCode = function(){ var hash = 5381; if (this.length === 0) return hash; for (var i = 0; i < this.length; i++) { var character = this.charCodeAt(i); hash = ((hash<<5)+hash)^character; // Convert to 32bit integer } return hash; }CommentedJun 24, 2015 at 9:54
  • What is the purpose of this part: return a & a? Shouldn't that just return a regardless?CommentedFeb 25, 2022 at 10:01
  • Not sure what you by regardless mean and I'm not expert on this stuff but this is a bitwise operator and is doing some math magic 🪄 hope that helps somehow 😅
    – Can Rau
    CommentedJul 9, 2022 at 2:53
25

UUID v3 and UUID v5 actually are hashes for a given input string.

  • UUID v3 is based on MD5,
  • UUID v5 is based on SHA-1.

So, the most obvious choice would be to go for UUID v5.

Fortunately, there is a popular npm package, which includes all UUID algorithms.

npm install uuid 

To actually generate a UUID v5, you need a unique namespace. This namespace acts like a seed, and should be a constant, to assure that for a given input the output will always be the same. Ironically, you should generate a UUID v4 as a namespace. And the easiest way to do so, is using some online tool.

Once you've got a namespace, you're all set.

import { v5 as uuidv5 } from 'uuid'; const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; const hash = uuidv5('input', MY_NAMESPACE); 

If your input string will always be an URL for instance, then there are some default namespaces which you can use.

const hashForURL = uuidv5('https://www.w3.org/', uuidv5.URL); 
2
  • 1
    Prefer this answer because it calls a library. Wonder if there's a way to make the generated ID shorter, say 10 chars? I looked up NanoID but it seems to lack the option to take an extra argument.CommentedDec 23, 2022 at 2:40
  • 3
    @YanKingYin part of the reason why it's so long, is to guarantee uniqueness. Which is an advantage of this answer. ;;;; However, uniqueness isn't always a requirement for a hash-code. If you don't need uniqueness, there's plenty of other algorithms that can give you shorter hashes. _(e.g. indexing algorithms sometimes use hashes that aren't unique. A query can then result in false-positive hits, which will be filtered by a strict compare afterwards.)
    – bvdb
    CommentedDec 23, 2022 at 16:10
15

Here is a compact ES6 friendly readable snippet

const stringHashCode = str => { let hash = 0 for (let i = 0; i < str.length; ++i) hash = Math.imul(31, hash) + str.charCodeAt(i) return hash | 0 } 
3
  • 1
    Can you explain why you used 31?
    – taseenb
    CommentedApr 5, 2021 at 16:20
  • 3
    cause it must be (hash << 5) - hash) which means (2^5 x hash - hash) = 32 x hash - hash = 31 x hash
    – amirhe
    CommentedApr 5, 2021 at 18:21
  • 1
    It looks like we can move | 0 to the return statement, because Math.imul() will do the convert in each loop.
    – muzuiget
    CommentedMay 20, 2021 at 21:34
14

I'm kinda late to the party, but you can use this module: crypto:

const crypto = require('crypto'); const SALT = '$ome$alt'; function generateHash(pass) { return crypto.createHmac('sha256', SALT) .update(pass) .digest('hex'); } 

The result of this function is always is 64 characters string; something like this: "aa54e7563b1964037849528e7ba068eb7767b1fab74a8d80fe300828b996714a"

    9

    My quick (very long) one liner based on FNV's Multiply+Xor method:

    my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16); 
    1
    • That's awesome, exactly what I was looking for, to generate a small hash based on a larger string. I needed to scroll so much for this solution, but was worth it!!!CommentedJun 23, 2020 at 6:46
    7

    Thanks to the example by mar10, I found a way to get the same results in C# AND Javascript for an FNV-1a. If unicode chars are present, the upper portion is discarded for the sake of performance. Don't know why it would be helpful to maintain those when hashing, as am only hashing url paths for now.

    C# Version

    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 Version

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

    SubtleCrypto.digest

    I’m not utilizing a server-side language so I can’t do it that way.

    Are you sure you can’t do it that way?

    Did you forget you’re using Javascript, the language ever-evolving?

    Try SubtleCrypto. It supports SHA-1, SHA-128, SHA-256, and SHA-512 hash functions.


    async function hash(message/*: string */) { const text_encoder = new TextEncoder; const data = text_encoder.encode(message); const message_digest = await window.crypto.subtle.digest("SHA-512", data); return message_digest; } // -> ArrayBuffer function in_hex(data/*: ArrayBuffer */) { const octets = new Uint8Array(data); const hex = [].map.call(octets, octet => octet.toString(16).padStart(2, "0")).join(""); return hex; } // -> string (async function demo() { console.log(in_hex(await hash("Thanks for the magic."))); })();

    2
    7

    The JenkinsOne at a Time Hash is quite nice:

    //Credits (modified code): Bob Jenkins (http://www.burtleburtle.net/bob/hash/doobs.html) //See also: https://en.wikipedia.org/wiki/Jenkins_hash_function //Takes a string of any size and returns an avalanching hash string of 8 hex characters. function jenkinsOneAtATimeHash(keyString) { let hash = 0; for (charIndex = 0; charIndex < keyString.length; ++charIndex) { hash += keyString.charCodeAt(charIndex); hash += hash << 10; hash ^= hash >> 6; } hash += hash << 3; hash ^= hash >> 11; //4,294,967,295 is FFFFFFFF, the maximum 32 bit unsigned integer value, used here as a mask. return (((hash + (hash << 15)) & 4294967295) >>> 0).toString(16) }; 

    Examples:

    jenkinsOneAtATimeHash('test') "31c25ec1" jenkinsOneAtATimeHash('a') "ca2e9442" jenkinsOneAtATimeHash('0') "6e3c5c6b" 

    You can also remove the .toString(16) part at the end to generate numbers:

    jenkinsOneAtATimeHash2('test') 834821825 jenkinsOneAtATimeHash2('a') 3392050242 jenkinsOneAtATimeHash2('0') 1849449579 

    Note that if you do not need to hash a string or key specifically, but just need a hash generated out of thin air, you can use:

    window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16) 

    Examples:

    window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16) "6ba9ea7" window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16) "13fe7edf" window.crypto.getRandomValues(new Uint32Array(1))[0].toString(16) "971ffed4" 

    and the same as above, you can remove the `.toString(16) part at the end to generate numbers:

    window.crypto.getRandomValues(new Uint32Array(1))[0] 1154752776 window.crypto.getRandomValues(new Uint32Array(1))[0] 3420298692 window.crypto.getRandomValues(new Uint32Array(1))[0] 1781389127 

    Note: You can also generate multiple values at once with this method, e.g.:

    window.crypto.getRandomValues(new Uint32Array(3)) Uint32Array(3) [ 2050530949, 3280127172, 3001752815 ] 
    1
    • nice . any idea how one would extend this to create a 16 char hash instead of 8?
      – Flion
      CommentedDec 30, 2024 at 12:33
    6

    A fast and concise one which was adapted from here:

    String.prototype.hashCode = function() { var hash = 5381, i = this.length while(i) hash = (hash * 33) ^ this.charCodeAt(--i) return hash >>> 0; } 
    1
    • Did you find any collision with this approach?
      – Shine
      CommentedNov 29, 2022 at 14:41
    5

    I needed a similar function (but different) to generate a unique-ish ID based on the username and current time. So:

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

    Produces:

    2DVFXJGEKL 6IZPAKFQFL ORGOENVMG ... etc 

    edit Jul 2022: As @canRau points out the authors of shortid prefer nanoid now https://github.com/ai/nanoid/

    5
    • 2
      @t0r0X well now I use a module called shortid: npmjs.com/package/shortid
      – jcollum
      CommentedJun 23, 2015 at 17:31
    • 1
      How are you using the username with shortid? It just seems to generate ids but I don't see how you are using to generate a hash from a stringCommentedSep 12, 2016 at 22:14
    • 1
      This answer has 3 downvotes. For the life of me I can't imagine why. No one has said anything... :-/
      – jcollum
      CommentedFeb 16, 2017 at 17:18
    • 1
      @jcollum that's why i almost never answer stale questions.. hit and runs go unnoticed. even after you fix up the answer, no one comes along to balance it out.
      – bryc
      CommentedDec 4, 2019 at 1:15
    • authors of shortid now recommend their improved version github.com/ai/nanoid
      – Can Rau
      CommentedJul 9, 2022 at 3:06
    3

    I have combined the two solutions (users esmiralha and lordvlad) to get a function that should be faster for browsers that support the js function reduce() and still compatible with old browsers:

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

    Example:

    my_string = 'xyz'; my_string.hashCode(); 
      3

      If you want to avoid collisions you may want to use a secure hash like SHA-256. There are several JavaScript SHA-256 implementations.

      I wrote tests to compare several hash implementations, see https://github.com/brillout/test-javascript-hash-implementations.

      Or go to http://brillout.github.io/test-javascript-hash-implementations/, to run the tests.

      1
      • 2
        Using a secure cryptographic hash can be extremely slow. Avoiding collisions is a product of the bit width, not security. 128 bit non-cryptographic hash or even 64 bits should be more than enough for most purposes. MurmurHash3_x86_128 is quite fast and has a very low chance of collisions.
        – bryc
        CommentedDec 9, 2018 at 3:44
      3

      I do not see any reason to use this overcomplicated crypto code instead of ready-to-use solutions, like object-hash library, or etc. relying on vendor is more productive, saves time and reduces maintenance cost.

      Just use https://github.com/puleos/object-hash

      var hash = require('object-hash'); hash({foo: 'bar'}) // => '67b69634f9880a282c14a0f0cb7ba20cf5d677e9' hash([1, 2, 2.718, 3.14159]) // => '136b9b88375971dff9f1af09d7356e3e04281951' 
      7
      • 3
        The source code of that lib isn't even readable.. just 50k of minified code.
        – bryc
        CommentedFeb 17, 2019 at 15:05
      • 1
        @bryc that's how vendor code supposed to look like :) and for sources you can check github.com/puleos/object-hash/blob/master/index.jsCommentedFeb 19, 2019 at 8:10
      • 1
        Minified code is 35.4 KB while the full source is 14.2 KB? That makes no sense.
        – bryc
        CommentedMar 19, 2019 at 16:35
      • 2
        @bryc have you considered this line? var crypto = require('crypto');. I think it adds this dependency code from the vendor in the minified version during a build.CommentedMar 20, 2019 at 18:00
      • 1
        If you really need to hash Objects, I wrote any-serialize to serialize ANY Object with sort keys, then cyrb53 to generate base36 hash.
        – Polv
        CommentedFeb 22, 2020 at 6:27
      2

      I went for a simple concatenation of char codes converted to hex strings. This serves a relatively narrow purpose, namely just needing a hash representation of a SHORT string (e.g. titles, tags) to be exchanged with a server side that for not relevant reasons can't easily implement the accepted hashCode Java port. Obviously no security application here.

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

      This can be made more terse and browser-tolerant with Underscore. Example:

      "Lorem Ipsum".hash() "4c6f72656d20497073756d" 

      I suppose if you wanted to hash larger strings in similar fashion you could just reduce the char codes and hexify the resulting sum rather than concatenate the individual characters together:

      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" 

      Naturally more risk of collision with this method, though you could fiddle with the arithmetic in the reduce however you wanted to diversify and lengthen the hash.

        2

        Another example use of SubtleCrypto.Digest():

        I wanted to generate a random color from the hash of a piece of text:

        const text = "some random piece of text"; crypto.subtle.digest('SHA-1', new TextEncoder().encode(text)).then(hash => { let hue = new DataView(hash).getUint32(0) % 360; let random_rgb = `hsl(${hue}, 100%, 50%)`; document.getElementById("bar").style.backgroundColor = random_rgb; });
        <div id="bar" style="width: 100px; height: 100px"> </div>

          1

          Slightly simplified version of @esmiralha's answer.

          I don't override String in this version, since that could result in some undesired behaviour.

          function hashCode(str) { var hash = 0; for (var i = 0; i < str.length; i++) { hash = ~~(((hash << 5) - hash) + str.charCodeAt(i)); } return hash; } 
            1

            Adding this because nobody did yet, and this seems to be asked for and implemented a lot with hashes, but it's always done very poorly...

            This takes a string input, and a maximum number you want the hash to equal, and produces a unique number based on the string input.

            You can use this to produce a unique index into an array of images (If you want to return a specific avatar for a user, chosen at random, but also chosen based on their name, so it will always be assigned to someone with that name).

            You can also use this, of course, to return an index into an array of colors, like for generating unique avatar background colors based on someone's name.

            function hashInt (str, max = 1000) { var hash = 0; for (var i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash = hash & hash; } return Math.round(max * Math.abs(hash) / 2147483648); } 
              1

              This generates a consistent hash based on any number of params passed in:

              /** * Generates a hash from params passed in * @returns {string} hash based on params */ function fastHashParams() { var args = Array.prototype.slice.call(arguments).join('|'); var hash = 0; if (args.length == 0) { return hash; } for (var i = 0; i < args.length; i++) { var char = args.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return String(hash); } 

              fastHashParams('hello world') outputs "990433808"

              fastHashParams('this',1,'has','lots','of','params',true) outputs "1465480334"

                1

                stable-hash

                A tiny and fast (481b unpkg) lib for "stably hashing" a JavaScript value. Originally created for SWR.

                import hash from 'stable-hash' hash(anyJavaScriptValueHere) // returns a string 
                  0

                  cyrb53 or cyrb53a are definitely the best options but if you are looking for something along the lines of Crypto.subtle.digest then I have this (as you can see it requires async):

                  /** * https://stackoverflow.com/a/78057133/74167 * @param input - string to hash * @returns 53-bit number */ async function hash(/* @type {string} */ input) { const buffer = new TextEncoder().encode(input) const view = new DataView(await crypto.subtle.digest("SHA-256", buffer)); const first32bits = view.getUint32(0, true); const next21bits = view.getUint32(4, true) & 0b111111111111111111111; return first32bits * 0x200000 + next21bits; } (async function() { console.log('hash for empty string =', await hash("")); console.log('hash for a =', await hash("a")); console.log('hash for space =', await hash(" ")); })()

                    0

                    Without any dependencies in the browser (also handles objects)

                    async function sha256(rawData) { const data = typeof rawData === 'object' ? JSON.stringify(rawData) : String(rawData); const msgBuffer = new TextEncoder().encode(data); const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } 

                    This function returns a promise, so use it like this:

                    sha256('your_data').then((hash) => { console.log(hash); }); 
                      -1

                      How can you hash or say encrypt it and then decrypt it

                      function encrypt(inputString, key) { let encryptedValue = ''; for (let i = 0; i < inputString.length; i++) { const charCode = inputString.charCodeAt(i); const encryptedCharCode = (charCode + key) % 128; // Assuming ASCII characters encryptedValue += String.fromCharCode(encryptedCharCode); } return encryptedValue; } function decrypt(encryptedString, key) { let decryptedValue = ''; for (let i = 0; i < encryptedString.length; i++) { const charCode = encryptedString.charCodeAt(i); const decryptedCharCode = (charCode - key + 128) % 128; // Assuming ASCII characters decryptedValue += String.fromCharCode(decryptedCharCode); } return decryptedValue; } // Example usage const inputString = "revenue"; const key = 5; // Encryption/Decryption key const encryptedValue = encrypt(inputString, key); console.log(`Encrypted value of '${inputString}' is: ${encryptedValue}`); const decryptedValue = decrypt(encryptedValue, key); console.log(`Decrypted value of '${encryptedValue}' is: ${decryptedValue}`); 

                        Start asking to get answers

                        Find the answer to your question by asking.

                        Ask question

                        Explore related questions

                        See similar questions with these tags.