Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
generate random UUIDs

UUID

Returns a random v4 UUID of the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, where each x is replaced with a random hexadecimal digit from 0 to f, and y is replaced with a random hexadecimal digit from 8 to b.

There's also @LeverOne's approach using iteration, which is one byte shorter.

function b(
a // placeholder
){
return a // if the placeholder was passed, return
? ( // a random number from 0 to 15
a ^ // unless b is 8,
Math.random() // in which case
* 16 // a random number from
>> a/4 // 8 to 11
).toString(16) // in hexadecimal
: ( // or otherwise a concatenated string:
[1e7] + // 10000000 +
-1e3 + // -1000 +
-4e3 + // -4000 +
-8e3 + // -80000000 +
-1e11 // -100000000000,
).replace( // replacing
/[018]/g, // zeroes, ones, and eights with
b // random hex digits
)
}
function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2011 Jed Schmidt <http://jed.is>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "UUID",
"description": "Generates random UUIDs",
"keywords": [
"UUID",
"ID",
"random"
]
}
@subzey

This comment has been minimized.

Copy link

commented Aug 12, 2011

We can use coercion and exponential notation to get template string and save 7 bytes:
function(){return(""+1e7+-1e3+-4e3+-8e3+-1e11).replace(/1|0/g,function(){return(0|Math.random()*16).toString(16)})}

@jed

This comment has been minimized.

Copy link
Owner Author

commented Aug 13, 2011

subzey, you are a genius. fixing and annotating now.

@jed

This comment has been minimized.

Copy link
Owner Author

commented Aug 13, 2011

you also inspired me to cut 10 more bytes out:

function b(a){return a?(0|Math.random()*16).toString(16):(""+1e7+-1e3+-4e3+-8e3+-1e11).replace(/1|0/g,b)}
@jed

This comment has been minimized.

Copy link
Owner Author

commented Aug 13, 2011

(0|Math.random()*16).toString(16)

can also be trimmed 4 bytes to

Math.random().toString(16)[2]

if you don't care about IE... thoughts?

@subzey

This comment has been minimized.

Copy link

commented Aug 13, 2011

@jed, Math.random() may return 0, so Math.random().toString(16)[2] may return undefined.

Also, isn't setting name of function affects the outer scope?

@jed

This comment has been minimized.

Copy link
Owner Author

commented Aug 13, 2011

well, with the chance of Math.random returning a zero 1 in 2<<61, seems like the definition of an edge case. more likely that IE barfs on it.

also, we're allowing named functions because we assume that they'll be assigned externally.

@subzey

This comment has been minimized.

Copy link

commented Aug 13, 2011

@jed, thanks for your clarification about function names, I didn't know that

@jed

This comment has been minimized.

Copy link
Owner Author

commented Aug 15, 2011

@subzey, i hope you use this knowledge to bring us some awesome new stuff.

@LeverOne

This comment has been minimized.

Copy link

commented Oct 6, 2011

""+1e7+-1e3+-4e3+-8e3+-1e11 --> [1e7]+-1e3+-4e3+-8e3+-1e11
it's а flaw of many of you.

@subzey

This comment has been minimized.

Copy link

commented Oct 7, 2011

@LeverOne, perfect! Thanks

@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 7, 2011

wow, that's crazy. good work @LeverOne.

@fgnass

This comment has been minimized.

Copy link

commented Oct 7, 2011

What about replacing /1|0/ with /\d/?

@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 7, 2011

the 4 is hardcoded according to the spec, and the 8 is a bit of a hack... technically it should be anything from 8 to b. so replacing those would make it less compliant.

@fgnass

This comment has been minimized.

Copy link

commented Oct 7, 2011

Ah I see, I missed that.

@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 7, 2011

i wonder if we finally have enough bytes to make it compliant, such that the 8 can be 8, 9, a, or b...

@subzey

This comment has been minimized.

Copy link

commented Oct 7, 2011

It is enough already :)

function c(a,b){return a?(~~b+0|Math.random()*16>>b/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/1|0|(8)/g,c)}
@subzey

This comment has been minimized.

Copy link

commented Oct 7, 2011

Or this, shorter:

function c(a,b){return a?(b|Math.random()*16>>b/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/1|0|(8)/g,c)}
@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 7, 2011

thanks again, @subzey. updated.

@broofa

This comment has been minimized.

Copy link

commented Oct 7, 2011

Line 20 of annotated.js should be 'c', not 'b'.

BTW, you're all insane. I love it! :)

@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 7, 2011

good catch! for those not in the know, @broofa is a celebrity in the world of UUIDs: https://github.com/broofa/node-uuid

@tsaniel

This comment has been minimized.

Copy link

commented Oct 9, 2011

Maybe save 4 bytes...?


@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 9, 2011

what the... how on earth does this work?

@tsaniel

This comment has been minimized.

Copy link

commented Oct 9, 2011

There are three possible values 0,1,8 for a.

For case 0: 0^Math.random()*16>>0/4, >>0/4 is actually >>0, just works as Math.floor.
The 0^ here does nothing.

For case 1: 1^Math.random()*16>>1/4, >>1/4 is still >>0.
Although 1^ here toggles the least significant bit, we can still use it to generate random numbers 0-15.

For case 8: 8^Math.random()*16>>8/4, >>8/4 turns out >>2.
The maximum number of Math.random()*16 in binary form is 1111, that means the range is 0000-1111.
By right shifting 2 bits, the maximum number of Math.random()*16>>2 is 0011, and the range, 0000-0011. What it does here is to generate random numbers 0-3.
Finally, because the most significant bit is always 0, 8^ is actually 8|, it is to ensure the range starts from 8. And now we get 8 + {0, 1, 2, 3}.

@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 9, 2011

incredible work, @tsaniel!

@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 9, 2011

it's amazing how this gist has evolved, from

function(){return"00000000-0000-4000-8000-000000000000".replace(/0/g,function(){return(0|Math.random()*16).toString(16)})}

to

function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}

while hitting all the greatest hits: recursion, coercion of a number into a string through an array (!), operator repurposing...

@tsaniel

This comment has been minimized.

Copy link

commented Oct 9, 2011

I love those tricks, especially the string coercion with array, though i love bitwise tricks.
By the way, i am still thinking if we can shorten /[018]/...

@atk

This comment has been minimized.

Copy link

commented Oct 9, 2011

@tsaniel - no way, the other way round would be /[^4-]/, which is just as long.

@tsaniel

This comment has been minimized.

Copy link

commented Oct 9, 2011

@atk: That's true. I mean if we can do something to use /\d/ instead.

@atk

This comment has been minimized.

Copy link

commented Oct 9, 2011

...which would require that the 4 will be left alone and enlarge the equation probably more than the 3 letters we saved.

@tsaniel

This comment has been minimized.

Copy link

commented Oct 9, 2011

Maybe that's the end.

@atk

This comment has been minimized.

Copy link

commented Oct 9, 2011

I fear it is.

@subzey

This comment has been minimized.

Copy link

commented Oct 11, 2011

Once again, excellent work, @tsaniel!

Sorry for my late comment, my mobile browser doesn't work well with github

@LeverOne

This comment has been minimized.

Copy link

commented Oct 24, 2011

Recursion & coercion vs. loop & math: https://gist.github.com/1308368

@jed

This comment has been minimized.

Copy link
Owner Author

commented Oct 24, 2011

just when you thought it couldn't get any shorter, @LeverOne sheds a byte. good job!

@tsaniel

This comment has been minimized.

Copy link

commented Oct 24, 2011

@LeverOne is my hero.

@kalupa

This comment has been minimized.

Copy link

commented Jun 20, 2013

damnit jed, i keep finding you everywhere. thanks for this!

@jussi-kalliokoski

This comment has been minimized.

Copy link

commented Jun 27, 2013

In Firefox you could use the single statement function body to shorten it a few bits still:

function b(a)a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)
@jussi-kalliokoski

This comment has been minimized.

Copy link

commented Jun 27, 2013

Shorter yet (cheats, ES6, and works only in Firefox!!!), and less leaky:

function()([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,a=>(a^Math.random()*16>>a/4).toString(16))
@jussi-kalliokoski

This comment has been minimized.

Copy link

commented Jun 27, 2013

Hah, since I already used ES6 and there are no variable assignments, might as well go all the way and avoid Firefox-only stuff:

()=>([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,a=>(a^Math.random()*16>>a/4).toString(16))
@jameswyse

This comment has been minimized.

Copy link

commented Jul 29, 2013

This is awesome!

Here's a benchmark vs node-uuid: http://jsperf.com/node-uuid-performance/11

@ronkorving

This comment has been minimized.

Copy link

commented Aug 22, 2013

I was so rooting for Crazy Gist to win that one :(

@thelinuxlich

This comment has been minimized.

Copy link

commented Oct 24, 2015

Is this relatively safe to generate UUIDs simultaneously between many clients?

@solderjs

This comment has been minimized.

Copy link

commented Nov 8, 2015

Make it secure at the cost of just a few bytes by replacing Math.random() * 16:

browser

crypto.getRandomValues(new Uint8Array(1))[0] % 16

function b(
  a                  // placeholder
){
  return a           // if the placeholder was passed, return
    ? (              // a random number from 0 to 15
      a ^            // unless b is 8,
      crypto.getRandomValues(new Uint8Array(1))[0]  // in which case
      % 16           // a random number from
      >> a/4         // 8 to 11
      ).toString(16) // in hexadecimal
    : (              // or otherwise a concatenated string:
      [1e7] +        // 10000000 +
      -1e3 +         // -1000 +
      -4e3 +         // -4000 +
      -8e3 +         // -80000000 +
      -1e11          // -100000000000,
      ).replace(     // replacing
        /[018]/g,    // zeroes, ones, and eights with
        b            // random hex digits
      )
}

node.js

crypto.randomBytes(1)[0] % 16

function b(
  a                  // placeholder
){
  return a           // if the placeholder was passed, return
    ? (              // a random number from 0 to 15
      a ^            // unless b is 8,
      crypto.randomBytes(1)[0]  // in which case
      % 16           // a random number from
      >> a/4         // 8 to 11
      ).toString(16) // in hexadecimal
    : (              // or otherwise a concatenated string:
      [1e7] +        // 10000000 +
      -1e3 +         // -1000 +
      -4e3 +         // -4000 +
      -8e3 +         // -80000000 +
      -1e11          // -100000000000,
      ).replace(     // replacing
        /[018]/g,    // zeroes, ones, and eights with
        b            // random hex digits
      )
}
@dalanmiller

This comment has been minimized.

Copy link

commented Nov 10, 2015

@coolaj89 I don't have crypto.randomBytes in Chrome 46.0.2490.80 but there is crypto.getRandomValues

@aichholzer

This comment has been minimized.

Copy link

commented Sep 5, 2016

And even smaller now:

b = a=>a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b);
@Kiodaddy

This comment has been minimized.

Copy link

commented Jun 20, 2017

I am having issue in 3rd file and i am not able to correct it .

@GraniteConsultingReviews

This comment has been minimized.

Copy link

commented Aug 27, 2017

Great This works well thanks

@fergusean

This comment has been minimized.

Copy link

commented Aug 30, 2017

in the annotated version:

-8e3 +         // -80000000 +

should be

-8e3 +         // -8000 +
@GraniteConsultingReviews

This comment has been minimized.

Copy link

commented Sep 7, 2017

I try this code but this is not generating random UUIDs

@Liath

This comment has been minimized.

Copy link

commented Oct 23, 2017

@coolaj89's you can golf that down a little:
([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b=>(b^crypto.rng(1)[0]%16>>b/4).toString(16))
crypto.rng is the same as crypto.randomBytes on a stock node.js build.

@Native-Coder

This comment has been minimized.

Copy link

commented Nov 22, 2017

You guys are freaking insane...I love it...

@sowmitranalla

This comment has been minimized.

Copy link

commented Dec 5, 2017

this was a wonderful thread to read

@ghost

This comment has been minimized.

Copy link

commented Dec 23, 2017

@coolaj89 for the browser version you can shave off another char by replacing:

new Uint8Array(1)

with (not supported in all browsers, of course):

Uint8Array.of(0)
@Louies89

This comment has been minimized.

Copy link

commented Jan 4, 2018

@jussi-kalliokoski
For Nodejs, below change has to be done:
()=>([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,a=>((a^crypto.randomBytes(1)[0]*16>>a/4).toString(16))[0])

@seanlindo

This comment has been minimized.

Copy link

commented Mar 3, 2018

This is fantastic.

@Elephant-Vessel

This comment has been minimized.

Copy link

commented Mar 7, 2018

Awesome. Is there a readable non-golfed version of this code?

@miniak

This comment has been minimized.

Copy link

commented Mar 17, 2018

you can use this `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}` to make it clear that it's a string

@mindplay-dk

This comment has been minimized.

Copy link

commented Apr 6, 2018

Awesome. Is there a readable non-golfed version of this code?

Here's a more readable (verbose, RFC-compliant) version using crypto API:

var hex = [];

for (var i = 0; i < 256; i++) {
    hex[i] = (i < 16 ? '0' : '') + (i).toString(16);
}

function makeUUID() {
    var r = crypto.getRandomValues(new Uint8Array(16));

    r[6] = r[6] & 0x0f | 0x40;
    r[8] = r[8] & 0x3f | 0x80;

    return (
        hex[r[0]] +
        hex[r[1]] +
        hex[r[2]] +
        hex[r[3]] +
        "-" +
        hex[r[4]] +
        hex[r[5]] +
        "-" +
        hex[r[6]] +
        hex[r[7]] +
        "-" +
        hex[r[8]] +
        hex[r[9]] +
        "-" +
        hex[r[10]] +
        hex[r[11]] +
        hex[r[12]] +
        hex[r[13]] +
        hex[r[14]] +
        hex[r[15]]
    );
}
@MikeFielden

This comment has been minimized.

Copy link

commented May 7, 2018

I have nothing excellent to offer other than I have REALLY enjoyed reading this :)

@pb-nati

This comment has been minimized.

Copy link

commented May 17, 2018

This is amazing. Small piece of code to do something simple.

@PaulCordo

This comment has been minimized.

Copy link

commented May 24, 2018

What's this [1e7] part for that Typescript refuse to compile saying "Operator '+' cannot be applied to types 'number[]' and '-1000'." ?

@AJamesPhillips

This comment has been minimized.

Copy link

commented May 29, 2018

@PaulCordo it's because these insane(ly amazing) authors want to convert 1e7 into a string and because [1e7]+-1e3 is 1 byte shorter than ""+1e7+-1e3! :) A bit more info and an example: https://stackoverflow.com/q/9300941/539490

@eric1iu

This comment has been minimized.

Copy link

commented Jun 15, 2018

wow! amazing!

@edo9k

This comment has been minimized.

Copy link

commented Jul 6, 2018

NICE!

@dehghani-mehdi

This comment has been minimized.

Copy link

commented Jul 21, 2018

After reading comments, I figure out I don't nothing about JS, really great job, thank you !!

@faazshift

This comment has been minimized.

Copy link

commented Jul 28, 2018

I came here from the link in the README of kelektiv/node-uuid. I've tested v4 of that implementation, including forcing it to use the non cryptographically safe Math.random() for random number generation, and found it to be pretty reliably unique over many millions of iterations. However, this mini UUID function pretty reliably runs into collisions after 20-30 million iterations. Not entirely sure what the design flaw is, but for UUIDs that need to be guaranteed unique, it's probably best not to use this function in its present form in mission-critical code. Other than that, nice work on creating such a compact ID generator.

@BerlinChan

This comment has been minimized.

Copy link

commented Sep 19, 2018

I like this LICENSE! That is awosome and so easy to understand!

@handicraftsman

This comment has been minimized.

Copy link

commented Sep 25, 2018

Lol

@LDubya

This comment has been minimized.

Copy link

commented Oct 29, 2018

Such collaboration. This is what the internet was made for.

@smurp

This comment has been minimized.

Copy link

commented Nov 2, 2018

Who's reading this at 2018-11-02T09:07:56Z ?

@rmpel

This comment has been minimized.

Copy link

commented Nov 9, 2018

I. F*KING. LOVE. THIS.

I would have NEVER EVER thought of doing this like this!

Amazing work!

@bengrunfeld

This comment has been minimized.

Copy link

commented Jan 11, 2019

Just to save a few more characters. ;)

let b=a=>a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)

@daCyberpunk

This comment has been minimized.

Copy link

commented Feb 3, 2019

Crazy guys! Awesome job! Lovely License! Thx

@Hypoon

This comment has been minimized.

Copy link

commented Feb 7, 2019

First thing's first: I am not a lawyer, and I couldn't care less about the legal details anyway, but others might, SOOOO...

The copyright line in the license applies to the wording of the license. Unless you authored the license, it shouldn't be your copyright. Leaving it as-is constitutes (presumably accidental) plagiarism. Am I wrong?

See http://www.wtfpl.net/faq/ for details.

I doubt anybody cares much, but other people are forking this and updating the copyright line with their own names, thereby perpetuating the error.

(With that said, this is an awesome way to generate a UUID. Thank you for sharing!)

@variadico

This comment has been minimized.

Copy link

commented Apr 7, 2019

What is the purpose of the placeholder function param a in function b(a)?

@Aaronius

This comment has been minimized.

Copy link

commented Apr 10, 2019

I can confirm what @faazshift has said, which is a bit worrisome. In four consecutive runs of attempting to generate 50 million unique IDs, I ran into collisions after:

  1. 22342746 IDs generated
  2. 24863680 IDs generated
  3. 32720420 IDs generated
  4. 24487480 IDs generated

In every run, all IDs generated after the first collision occurred were also collisions. This is the script I used for testing:

function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}

const sets = [];

let currentSet;
let breakAtIndex;

for (let i = 0; i < 50000000; i++) {
  if (i % 1000000 === 0) {
    console.log(`${i} IDs generated`);
    // Generate a new set so that we don't run into max size limits
    currentSet = new Set();
    sets.push(currentSet);
  }

  const id = b();
  if (sets.find(set => set.has(id))) {
    console.log(`Conflict: ${id} at index ${i}`);

    if (!breakAtIndex) {
      breakAtIndex = i + 10;
    }
  }

  if (breakAtIndex === i) {
    break;
  }

  currentSet.add(id);
}

console.log("Done");
@standardizer

This comment has been minimized.

Copy link

commented Jul 16, 2019

I can confirm what @faazshift has said, which is a bit worrisome. In four consecutive runs of attempting to generate 50 million unique IDs, I ran into collisions after:

  1. 22342746 IDs generated
  2. 24863680 IDs generated
  3. 32720420 IDs generated
  4. 24487480 IDs generated

In every run, all IDs generated after the first collision occurred were also collisions. This is the script I used for testing:

function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}

const sets = [];

let currentSet;
let breakAtIndex;

for (let i = 0; i < 50000000; i++) {
  if (i % 1000000 === 0) {
    console.log(`${i} IDs generated`);
    // Generate a new set so that we don't run into max size limits
    currentSet = new Set();
    sets.push(currentSet);
  }

  const id = b();
  if (sets.find(set => set.has(id))) {
    console.log(`Conflict: ${id} at index ${i}`);

    if (!breakAtIndex) {
      breakAtIndex = i + 10;
    }
  }

  if (breakAtIndex === i) {
    break;
  }

  currentSet.add(id);
}

console.log("Done");

@faazshift and @Aaronius, which browser did you run the script in?
Using Firefox Version 68.0 (64 Bit) on Windows 10 Version 1903 (64 Bit), I created more than 65 Million UUIDs without any collision.
The function b() seem fine for me, so Math.random() must have been the culprit.

@Aaronius

This comment has been minimized.

Copy link

commented Jul 16, 2019

Chrome on Mac Mojave. I don't remember which version of Chrome, but it would have been a recent one.

@Aaronius

This comment has been minimized.

Copy link

commented Jul 16, 2019

I just tried again on the following:

Chrome 75.0.3770.100 (64 bit)
Safari 12.0 (14606.1.36.1.9)
Firefox 67.0.4 (64 bit)

all on Mac Mojave OS. I was able to successfully generate 50 million unique IDs on all of them. I'm not sure what changed, but that's good news!

@faazshift

This comment has been minimized.

Copy link

commented Jul 16, 2019

@standardizer Node.js, but I validated the collision in Chrome on linux. I just reproduced the collision in the latest version of Node.js.

@stefanovazzocell

This comment has been minimized.

Copy link

commented Jul 16, 2019

Would this work?

b=(a)=>(a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b))

optionally starting with var or const

var b=(a)=>(a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b))

PS: Didn't get time to read all the comments, sorry if this was already discussed.

@standardizer

This comment has been minimized.

Copy link

commented Jul 17, 2019

@standardizer Node.js, but I validated the collision in Chrome on linux. I just reproduced the collision in the latest version of Node.js.

@faazshift You wrote, you found it [the Math.random() return value] to be pretty reliably unique over many millions of iterations. Could you provide your test script, please?

@faazshift

This comment has been minimized.

Copy link

commented Jul 17, 2019

@standardizer In my original comment, I was referring to v4 of the uuid package available via npm. That package contains a link to this gist.

In my test case back when I made that determination, I used a copied and pared down version of this:

https://github.com/kelektiv/node-uuid/blob/master/v4.js

By using Math.random() for random number generation, I was referring to using this in my pared down test code:

https://github.com/kelektiv/node-uuid/blob/master/lib/rng-browser.js#L26-L33

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.