1

How can I randomly generate a 160 bits (20 bytes) long hexadecimal as a string (same length as Git commit hashes), for use as a unique identifier?

Max888
  • 3,089
  • 24
  • 55
  • 2
    I assume that if you want them be randomly it means that you need them as identifier. If so why not just using a unique id? – B001ᛦ Mar 18 '20 at 11:23
  • "*like the ones used for Git commits*" git commit hashes are NOT random but based on the contents of the commit. This is key - the same commit *always* has the same hash, but if you change the information that goes in a commit, you get a new hash. This means that you can always check commits for equality by the hash. A lot of operations are tied to that in git. So, do you want it *really* like that where your hex value would be based on another data or do you truly want random one? – VLAZ Mar 18 '20 at 11:25
  • @B001ᛦ I've updated the question to clarify your point – Max888 Mar 18 '20 at 11:31
  • Do you need 160 bits? https://en.wikipedia.org/wiki/Universally_unique_identifier – Felix Kling Mar 18 '20 at 11:31
  • @VLAZ I've updated the question to clarify your point – Max888 Mar 18 '20 at 11:31
  • 1
    `[...crypto.getRandomValues(new Uint8Array(20))].map(m=>('0'+m.toString(16)).slice(-2)).join('')` – Keith Mar 18 '20 at 11:32
  • Look at [create-guid-uuid-in-javascript](https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript) – B001ᛦ Mar 18 '20 at 11:32
  • 2
    @Keith this works - why not post as answer then I can accept? – Max888 Mar 18 '20 at 11:33
  • NB: check availability of the `crypto` API in all anticipated browsers before committing to that solution. – Alnitak Mar 18 '20 at 11:36

2 Answers2

5

There is cyrpto.getRandomValues in modern browser, support is pretty good. -> https://caniuse.com/#feat=getrandomvalues

So using this you could do ->

const a = [...crypto.getRandomValues(new Uint8Array(20))].map(m=>('0'+m.toString(16)).slice(-2)).join('');

console.log(a);
Keith
  • 22,005
  • 2
  • 27
  • 44
  • 1
    If you ignore MSIE (or poly fill it) you can use: `const a = [...crypto.getRandomValues(new Uint32Array(5))].map(m => m.toString(16).padStart(8, '0')).join('');` – Alnitak Mar 18 '20 at 11:41
  • or generate twice as many bits as you need and only convert the least-significant nybble to avoid the string padding: `[...crypto.getRandomValues(new Uint32Array(40))].map(m => (m & 15).toString(16)).join('')` – Alnitak Mar 18 '20 at 11:58
  • @Alnitak Your first idea is a good idea if performance is something your after, 32bit Op's are often faster than 8bit. But your second one is likely to be slower, as generating random numbers has a cost, and that cost is likely to be higher than a slice / pad. – Keith Mar 18 '20 at 12:14
  • I was going more for readability than performance but on my laptop the nybble version is 7% slower than the uint32 version, but the uint8 pad/slice version is 11% slower. https://jsperf.com/crypto-speed – Alnitak Mar 18 '20 at 12:51
  • This is the best for randomness values on server side. – S To Apr 18 '21 at 22:08
1

This should do the trick:

let characters = "0123456789abcdef"
let str = ""
for(let i = 0; i < 40; i++){
    str += characters[Math.floor(Math.random() * 16)]
}
console.log(str)
Delphi1024
  • 183
  • 1
  • 4