如何生成像“ aX4j9Z”这样的短 uid (在 JS 中)

对于我的 web 应用程序(在 JavaScript 中) ,我希望生成简短的 guid (针对不同的对象——实际上是不同的类型——字符串和字符串数组)

我需要一些类似于“ aX4j9Z”的东西作为我的 uids (guids)。

因此,这些 uid 对于 web 传输和 js 字符串处理应该足够轻量级,对于不大的结构(不超过10k 个元素)应该相当独特。说到“非常独特”,我的意思是,在生成 uid 之后,我可以检查这个 uid 是否已经存在于结构中,如果存在,我可以重新生成它。

93342 次浏览

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 = {};

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.

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;
}

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;
}
}());

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

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.

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

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.

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

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


console.log(uuid);

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

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

This is an old question and there are some good answers, however I notice that we are in 2022 and we can use ES6 and if you don't like to depend on 3rd party libs. Here is a solution for you.

I implemented a very simple generator using the build-in functions that JavaScript offers to us these days. We will use Crypto.getRandomValues() and Uint8Array() so check 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] this in total are 62 characters if we add _ and - it will complete 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