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?
Asked
Active
Viewed 2,675 times
1
-
2I 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 Answers
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
-
1If 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
-
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