How to generate short uid like "aX4j9Z" (in JS)
Asked Answered
S

13

114

For my web application (in JavaScript) I want to generate short guids (for different objects - that are actually different types - strings and arrays of strings)

I want something like "aX4j9Z" for my uids (guids).

So these uids should be lightweight enough for web transfer and js string processing and quite unique for not a huge structure (not more than 10k elements). By saying "quite unique" I mean that after the generation of the uid I could check whether this uid does already exist in the structure and regenerate it if it does.

Szymanski answered 6/6, 2011 at 6:52 Comment(2)
What is a "uid" and a "guid"? The simplest way to generate unique values is to start with a string like "x" then append a number generated by a counter, so you get "x0", "x1", and so on. What is the context for "unique"? Element ids and names? Properties of some object? Other?Foetid
Just putting a GISTS search link regarding this topic: gist.github.com/search?l=JavaScript&q=unique+idMorgen
S
153

See @Mohamed's answer for a pre-packaged solution (the shortid package). Prefer that instead of any other solutions on this page if you don't have special requirements.


A 6-character alphanumeric sequence is pretty enough to randomly index a 10k collection (366 = 2.2 billion and 363 = 46656).

function generateUID() {
    // I generate the UID from two parts here 
    // to ensure the random number provide enough bits.
    var firstPart = (Math.random() * 46656) | 0;
    var secondPart = (Math.random() * 46656) | 0;
    firstPart = ("000" + firstPart.toString(36)).slice(-3);
    secondPart = ("000" + secondPart.toString(36)).slice(-3);
    return firstPart + secondPart;
}

UIDs generated randomly will have collision after generating ~ √N numbers (birthday paradox), thus 6 digits are needed for safe generation without checking (the old version only generates 4 digits which would have a collision after 1300 IDs if you don't check).

If you do collision checking, the number of digits can be reduced 3 or 4, but note that the performance will reduce linearly when you generate more and more UIDs.

var _generatedUIDs = {};
function generateUIDWithCollisionChecking() {
    while (true) {
        var uid = ("0000" + ((Math.random() * Math.pow(36, 4)) | 0).toString(36)).slice(-4);
        if (!_generatedUIDs.hasOwnProperty(uid)) {
            _generatedUIDs[uid] = true;
            return uid;
        }
    }
}

Consider using a sequential generator (e.g. user134_item1, user134_item2, …) if you require uniqueness and not unpredictability. You could "Hash" the sequentially generated string to recover unpredictability.

UIDs generated using Math.random is not secure (and you shouldn't trust the client anyway). Do not rely on its uniqueness or unpredictability in mission critical tasks.

Smattering answered 6/6, 2011 at 6:59 Comment(14)
Nice. That's very clean. Could you explain why you + "1000" at the end?Nonperformance
@Nonperformance - in the off-chance that it generates a random number of "0", or ".000000000001", etc. and the final string ends up like "4z". The "0000" ensures that it's always at least 4-characters longHanselka
@KennyTM - >> 0 is buggy in safari. Use << 0 or |0Hanselka
For each 10,000 numbers, there's at least a 1:160 chance that two ids will be the same. The substr could be longer, but why use random numbers at all?Foetid
@RobG: Because OP wants a "short" UID.Smattering
@KennyTM - so why use a random number at all? Using a counter and values 0-9,a-z, A-Z gives 62^3 possible combinations (238,328)) with uniqueness guaranteed and only 3 characters.Foetid
@Foetid - A simple counter that goes "n1" --> "n10000" and on would work as well. I was going on the assumption that the OP had a reason for not doing thatHanselka
@RobG: That works well if it needs to be unique only for the client in one session ensuring no race condition.Smattering
@KennyTM - how can there be a race condition in plain javascript? See my answer, can you see any chance of that? The only possibility I see is if it's used with an XHR callback, then the IDs may not be squential with the XHR calls (given they may return out of sequence). That can be fixed by getting the ID before making the call. Oh, and uniqueness can only be guaranteed for the client, otherwise it has to be done on the server.Foetid
Well, Thank you I believe its very good method. I'll be trying to use it.Szymanski
this does not work. I've tested the code and there is a small chance of getting the same uuid, if you do something like generate 10,000 at once. This is not a true UUID generator. Math.random() works but is not tiny.Batfowl
@ChrisScott Yes there is 2% collision probability among 10000 IDs. Generating 7 digits reduces it to 0.06%. But there is always a small chance of collision as long as the IDs are randomly generated with no correlation.Smattering
For small blocks of random ID's this works ++, short and sweet without the need for downloading an external lib. Currently using to generate IDs for dynamically created HTML elements.Coronary
Please, don't ever use the generateUIDWithCollisionChecking() function: it generates a nasty memory-leak which will also degrade the performance of the function. If your page stays open for a while (e.g. single page application) it will be a problem soon or later.Shermanshermie
S
79

Update 08/2020:

shortid has been deprecated in favor of nanoid which is smaller and faster:

  • Small. 108 bytes (minified and gzipped). No dependencies. Size Limit controls the size.
  • Fast. It is 40% faster than UUID.
  • Safe. It uses cryptographically strong random APIs. Can be used in clusters.
  • Compact. It uses a larger alphabet than UUID (A-Za-z0-9_-). So ID size was reduced from 36 to 21 symbols.
  • Portable. Nano ID was ported to 14 programming languages.
import { nanoid } from 'nanoid'

// 21 characters (default)
// ~149 billion years needed, in order to have a 1% probability of at least one collision.
console.log(nanoid()) //=> "V1StGXR8_Z5jdHi6B-myT"

// 11 characters
// ~139 years needed, in order to have a 1% probability of at least one collision.
console.log(nanoid(11)) //=> "bdkjNOkq9PO"

More info here : https://zelark.github.io/nano-id-cc/


Old answer

There is also an awesome npm package for this : shortid

Amazingly short non-sequential url-friendly unique id generator.

ShortId creates amazingly short non-sequential url-friendly unique ids. Perfect for url shorteners, MongoDB and Redis ids, and any other id users might see.

  • By default 7-14 url-friendly characters: A-Z, a-z, 0-9, _-
  • Non-sequential so they are not predictable.
  • Supports cluster (automatically), custom seeds, custom alphabet.
  • Can generate any number of ids without duplicates, even millions per day.
  • Perfect for games, especially if you are concerned about cheating so you don't want an easily guessable id.
  • Apps can be restarted any number of times without any chance of repeating an id.
  • Popular replacement for Mongo ID/Mongoose ID.
  • Works in Node, io.js, and web browsers.
  • Includes Mocha tests.

Usage

var shortid = require('shortid');
console.log(shortid.generate()); //PPBqWA9
Swipple answered 17/12, 2016 at 8:43 Comment(7)
This should be the accepted answer in my opinion. Another option, which is nowhere near as as good, is of course replacing the hyphens with spaces, replace(/[-]/g, ''), which gets down to a length of 32.Personalism
Very much disagree that "download a potentially harmful package" should be the answer to programming questions.Kroll
I also strongly disagree. Devs come here for code answers and to learn, not to see download links to NPM packages, regardless of their perfection. For my use-case I cannot use any package, and must integrate a solution within the code itself.Morgen
I disagree with the disagreers! The OP wants UIDs. The OP does not express your additional constraints. For most people, a well-tested library is better than reinventing the wheel, and is the correct solution. If you have a different problem, you can post a different question or an alternative answer.Patrinapatriot
@AjahnCharles But you would surely disagree with the statement that this should be the accepted answer, you would not? It's a valid solution that has its right here, but it's not an accepted answer, rightfully.Fluky
@MartinBraun - This is probably for meta to decide; and I don't have a strong opinion. I would errr towards what the OP/question is looking for: a real-world solution or a lecture. I'd venture most people end up here looking for a solution. I think it's also relevant that the reference is a link to GitHub, so you can see the complete code of a real-world battle-tested solution.Patrinapatriot
@Mohamed Ramrami : the result of nanoid() is unique?Viper
F
38

Here is a one liner, but it gives only lowercase letters and numbers:

var uuid = Math.random().toString(36).slice(-6);

console.log(uuid);
Fluky answered 25/1, 2020 at 22:14 Comment(2)
One can feed in Date.now() to get a sequence with some meaning: Math.floor(Date.now() / 1000).toString(36);Centroclinal
@Centroclinal be aware that this will cause a lot of duplicates.Featherbedding
B
14

Get a simple counter to start from 100000000, convert the number into radix 36.

(100000000).toString(36);  //1njchs

(2100000000).toString(36); //yqaadc

You can comfortably have 2 billion elegant unique ids, just like YouTube

Blythebm answered 2/10, 2020 at 3:47 Comment(3)
i found this helpful; nice not to have to download a whole package or add a function for this. i used milliseconds since epoch: (Math.round(Date.now())).toString(36)Grange
@Grange Something to consider is that if you are processing a lot of data all at once, you'll frequently generate the same ID. I hit batches of about 50 duplicated IDs at a time using for (let i = 0; i < 1000; i++) { console.log(Math.round(Date.now()).toString(36)); } typed straight into the Chrome browser's console. This will unnecessarily throttle your ability to process data if doing a lot all at once.Babiche
@Grange I'm not sure why you're wrapping Date.now() with Math.round() as Date.now() will return an integer. Otherwise, a natural solution for when an ID is exclusively only needed once only every so often.Babiche
F
5

The following generates 62^3 (238,328) unique values of 3 characters provided case sensitivity is unique and digits are allowed in all positions. If case insensitivity is required, remove either upper or lower case characters from chars string and it will generate 35^3 (42,875) unique values.

Can be easily adapted so that first char is always a letter, or all letters.

No dobut it can be optimised, and could also refuse to return an id when the limit is reached.

var nextId = (function() {
  var nextIndex = [0,0,0];
  var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
  var num = chars.length;

  return function() {
    var a = nextIndex[0];
    var b = nextIndex[1];
    var c = nextIndex[2];
    var id = chars[a] + chars[b] + chars[c];

    a = ++a % num;

    if (!a) {
      b = ++b % num; 

      if (!b) {
        c = ++c % num; 
      }
    }
    nextIndex = [a, b, c]; 
    return id;
  }
}());
Foetid answered 6/6, 2011 at 7:44 Comment(0)
T
5

Considering the actual power of ES6 features we can generate hash IDs in JavaScript without relying on third-party libraries.

I implemented a very simple generator using the build-in functions that JavaScript offers to us these days. This implementation uses Crypto.getRandomValues() and Uint8Array() as shown in the code below:

const hashID = size => {
  const MASK = 0x3d
  const LETTERS = 'abcdefghijklmnopqrstuvwxyz'
  const NUMBERS = '1234567890'
  const charset = `${NUMBERS}${LETTERS}${LETTERS.toUpperCase()}`.split('')

  const bytes = new Uint8Array(size)
  crypto.getRandomValues(bytes)

  return bytes.reduce((acc, byte) => `${acc}${charset[byte & MASK]}`, '')
}

console.log({id: hashID(6)})

This implementation uses these characters: [A-Z], [a-z], [0-9] in total they are 62 characters, if we add _ and - it will complete up to 64 characters like this:

const hashID = size => {
  const MASK = 0x3d
  const LETTERS = 'abcdefghijklmnopqrstuvwxyz'
  const NUMBERS = '1234567890'
  const charset = `${NUMBERS}${LETTERS}${LETTERS.toUpperCase()}_-`.split('')

  const bytes = new Uint8Array(size)
  crypto.getRandomValues(bytes)

  return bytes.reduce((acc, byte) => `${acc}${charset[byte & MASK]}`, '')
}

console.log(`id: ${hashID(6)}`)

Note:

It will take around 2 days in order to have a 1% probability of at least one collision for 1000 IDs generated per hour with ID length of 6 characters. Keep this in mind when it is implemented into your project.

Threatt answered 28/9, 2022 at 9:17 Comment(2)
Also, check out the widely supported crypto.randomUUID method: developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUIDFluxion
No, actually the user asked how to generate a 6 character long hash string that can be used using JavaScript. randomUUID is a function that generates a string containing 36 characters long UUID. Second, it needs to use HTTPS. So it is to specific and probably not useful to the user that asked the questionThreatt
R
4
var letters = 'abcdefghijklmnopqrstuvwxyz';
var numbers = '1234567890';
var charset = letters + letters.toUpperCase() + numbers;

function randomElement(array) {
    with (Math)
        return array[floor(random()*array.length)];
}

function randomString(length) {
    var R = '';
    for(var i=0; i<length; i++)
        R += randomElement(charset);
    return R;
}
Rothman answered 6/6, 2011 at 7:6 Comment(5)
please explain reason for downvoting a correct and elegant answer, while not downvoting the other similar answer, thanksRothman
I wasn't the down-vote, but I'm almost willing to give another simply for the with(Math) evilness :)Hanselka
@cwolves - I think with has the potential to be used without issue in non-performance code, and that the "with is EVIL" is easily taken to an extreme. =) Neither is performance a factor (if it is merely don't use it), nor is creating or clobbering variables an issue (no assignments are made), nor is confusion with a global variables an issue here. I would rather take a small performance hit than have to redefine the entire Math module in global scope.Rothman
@cwolves - actually nevermind, I just realized that if one does with (Math) and one defines a variable var max = ... then one will overwrite Math.max......... okay not using with anymoreRothman
I don't really care about the performance of it, it's more the nuances of it, and the fact that you have to go searching previous lines of code in order to determine what floor and random actually refer toHanselka
B
2

This will generate a sequence of unique values. It improves on RobG's answer by growing the string length when all values have been exhaused.

var IdGenerator = (function () {

    var defaultCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()_-+=[]{};:?/.>,<|".split("");

    var IdGenerator = function IdGenerator(charset) {
        this._charset = (typeof charset === "undefined") ? defaultCharset : charset;
        this.reset();
    };

    IdGenerator.prototype._str = function () {
        var str = "",
            perm = this._perm,
            chars = this._charset,
            len = perm.length,
            i;
        for (i = 0; i < len; i++) {
            str += chars[perm[i]];
        }
        return str;
    };

    IdGenerator.prototype._inc = function () {
        var perm = this._perm,
            max = this._charset.length - 1,
            i;
        for (i = 0; true; i++) {
            if (i > perm.length - 1) {
                perm.push(0);
                return;
            } else {
                perm[i]++;
                if (perm[i] > max) {
                    perm[i] = 0;
                } else {
                    return;
                }
            }
        }
    };

    IdGenerator.prototype.reset = function () {
        this._perm = [];
    };

    IdGenerator.prototype.current = function () {
        return this._str();
    };

    IdGenerator.prototype.next = function () {
        this._inc();
        return this._str();
    };

    return IdGenerator;

}).call(null);

Usage:

var g = new IdGenerator(),
    i;

for (i = 0; i < 100; i++) {
   console.log(g.next());
}

This gist contains the above implementation and a recursive version.

Budde answered 16/10, 2013 at 18:14 Comment(0)
H
1

just randomly generate some strings:

function getUID(len){
    var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
          out = '';

    for(var i=0, clen=chars.length; i<len; i++){
       out += chars.substr(0|Math.random() * clen, 1);
    }

    // ensure that the uid is unique for this page
    return getUID.uids[out] ? getUID(len) : (getUID.uids[out] = out);
}
getUID.uids = {};
Hanselka answered 6/6, 2011 at 6:55 Comment(6)
It seems inefficient to generate random strings then have to test to see if they are unique. It is quite simple to generate unique strings (where unique has some scope or context), either with or without a randomly generated component using a simple counter.Foetid
@Foetid - The odds of one of these being non-unique is absurdly low if you're generating 6-digit keys (there are 56.8 billion unique keys with this). There will almost never be a collision so there will almost never be re-generation.Hanselka
@cwolves - why leave any chance when it can be simply avoided? And why generate a value that must then be checked for uniqueness when you can generate a guaranteed unique value in the first place?Foetid
@Foetid - because the OP may not want "000", "001", ... "00z", "00A", ... and generating these randomly is the simple way around that. Even if you were to hash them, you still need basic collision detection of the hash. Also, these may be being used between page loads, etc in which case you don't always want to start at 1. My general argument is that if the OP just wants a guid for the page, a simple counter works fine. Since the OP didn't ask for a counter, providing a base-62 counter isn't that useful either.Hanselka
The OP can generate the required number of IDs and randomly assign them so they aren't sequential (not specified in the question, but maybe it's needed).Foetid
@Foetid - And that's better than a 1/5.6 million chance of re-generating a key how? :) I still argue that if the OP wants 'guids', generating sequential keys isn't a good solution. But doesn't matter, OP's problem :)Hanselka
L
1

You can shorten a GUID to 20 printable ASCII characters without losing information or the uniqueness of the GUID.

Jeff Atwood blogged about that years ago:
Equipping our ASCII Armor

Liselisetta answered 19/2, 2012 at 14:33 Comment(0)
D
1

This solution combines Math.random() with a counter.

Math.random() should give about 53 bits of entropy (compared with UUIDv4's 128), but when combined with a counter should give plenty enough uniqueness for a temporary ID.

let _id_counter = 0
function id() {
  return '_' + (_id_counter++).toString(36) + '_' + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36)
}

console.log(Array.from({length: 100}).map(() => id()))

Features:

  • Simple implementation
  • Output of about 13 chars
  • Case-insensitive
  • Safe for use as HTML id and React key
  • Not suitable for database storage
Deneb answered 19/7, 2021 at 1:47 Comment(0)
B
0

You can use the md5 algorithm for generating a random string. md5 is the node package

 var randomChars = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 2);
 var shortUrl = md5(originalUrl + randomChars + new Date()).substring(0, 5).toString();
 console.log(shortUrl);

This will generate unique string every time.

Blossom answered 19/1, 2020 at 3:39 Comment(1)
@Morgen Is it the right solution? md5 package for Node.Blossom
A
0

I use this TypeScript function to create unique identifiers in my database that are much more readable than UUIDs. Note that when inserting records I catch the duplicate key exception and retry with a new ID.

const idChars: string = 'ABCDEFGHJKMNPQRSTUVWXYZ'

export function generateId(): string {
  const now: Date = new Date()
  let id = now.getUTCFullYear().toString()
  id += now.getUTCMonth().toString().padStart(2, '0')
  id += now.getUTCDay().toString().padStart(2, '0')
  for (let i = 0; i < 6; i++) id += idChars[Math.floor(Math.random() * idChars.length)]
  return id
}

It generates ids like 20230506VJDMQD.

The date prefix helps a lot with uniqueness, especially if you create thousands of records in the database over a long period of time. It's also super helpful for things like customer numbers or invoice numbers, where the date part provides additional information, not just uniqueness.

It's very easy to adapt this to any set of characters you prefer, and if you don't want the date prefix it's easy to remove that part from the code.

If you need to make millions of IDs per day, then you could increase the loop count from 6 to a bigger number, but at some point you might as well just use UUIDs.

If you really want just 6 characters, then the simplified version in JavaScript is

const idChars = 'ABCDEFGHJKMNPQRSTUVWXYZ'

function generateId() {
  let id = ''
  for (let i = 0; i < 6; i++) id += idChars[Math.floor(Math.random() * idChars.length)]
  return id
}

Anosmia answered 10/6, 2023 at 6:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.