0

I'm trying to generic a universally unique alphanumeric string in javascript. I've tried to use UUID but the format isn't desirable. Currently I am doing the following:

Math.floor((Math.random() * Date.now())).toString(16).toUpperCase()

This results in a string like: 4198A8BEA4. This is the desired format and relative length.

Can I be sure that this will always return a unique alphanumeric string?

If so, how can I be sure?

Sheldon
  • 175
  • 9
  • 2
    You cannot be sure. – Pointy Oct 28 '20 at 01:53
  • It is possible a random number and a moment in time to generate the same string..... – epascarello Oct 28 '20 at 01:54
  • Can you explain why? @Pointy – Sheldon Oct 28 '20 at 01:55
  • It may be *highly unlikely* that the current timestamp and the value returned by the `.random()` function produce a product that repeats a previous product, but it is not impossible. – Pointy Oct 28 '20 at 01:57
  • Do you have any suggestions on how to achieve the desired result as mentioned above? @Pointy – Sheldon Oct 28 '20 at 01:58
  • 1
    Side note: The whole point of using a UUID is that statistically speaking, the probability of generating the same UUID twice _is_ really zero. For 10 char string, the probability of duplication is much higher (albeit still rather low). – Tim Biegeleisen Oct 28 '20 at 01:59
  • The closest you can get to truly reliable is to use some value returned from a service that is guaranteed (to the greatest extent possible) to be a monotonically increasing value, or else a cryptographically-secure random value with a *lot* of bits, like 128 at least. – Pointy Oct 28 '20 at 02:05
  • And `Math.random()` is not a cryptographically-secure random number source. – Pointy Oct 28 '20 at 02:05
  • A service with a robust international time synchronization implementation (like NTP) that *never goes backwards* and which can produce high-resolution millisecond (or better) timestamps would be a good start towards something pretty reliable, though even that depends on site traffic load. – Pointy Oct 28 '20 at 02:07

1 Answers1

1

Nope, the resulting string will probably have 10 or 11 characters, and the characters will vary from 1 to F, so there are 16 ** 10 = 1099511627776 possibilities.

The likelihood of a collision is low - it'll probably be good enough for most code - but it's not zero.

To guarantee no collisions, the easiest way would be to use numeric indicies instead, and increment each time a new one is needed.

let i = 0;
const getNextUnique = () => i++;

console.log(getNextUnique());
console.log(getNextUnique());

The index is easy to see in the result, though. If that's a problem for you, use a one-way hash function without collisions on it. (though, even cryptographic hash functions can have collisions, depending on the algorithm)

SHA-256 does not have any known collisions, and such a collision is considered near impossible under current circumstances, so you could use it if you wanted:

let i = 0;
const getNextUnique = async () =>
  Array.prototype.map
    .call(
      new Uint8Array(
        await crypto.subtle.digest("SHA-256", new TextEncoder().encode(i++))
      ),
      (x) => ("0" + x.toString(16)).slice(-2)
    )
    .join("");

(async () => {
  console.log(await getNextUnique());
  console.log(await getNextUnique());
})();
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320