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


73

对于我的Web应用程序(使用JavaScript),我想生成简短的指导(针对不同的对象-实际上是不同的类型-字符串和字符串数组)

我的uid(向导)需要类似“ aX4j9Z”的名称。

因此,这些uid应该足够轻便,可以进行Web传输和js字符串处理,并且对于不大的结构(不超过10k个元素)来说应该是非常独特的。说“非常独特”是指在生成uid之后,我可以检查该uid是否已经存在于结构中,如果存在则重新生成。


2
什么是“ uid”和“ guid”?生成唯一值的最简单方法是以“ x”之类的字符串开头,然后附加一个计数器生成的数字,这样便得到“ x0”,“ x1”,依此类推。“独特”的上下文是什么?元素ID和名称?一些对象的属性?其他?
RobG 2011年

只是把一学家搜索关于该主题的链接:gist.github.com/search?l=JavaScript&q=unique+id
VSYNC

Answers:


126

请参阅@Mohamed的答案以获取预打包的解决方案(shortidpackage)。如果您没有特殊要求,请优先使用此页面上的其他解决方案。


6个字符的字母数字序列足以随机索引10k集合(36 6 = 22亿,36 3 = 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;
}

随机生成的UID在生成〜√N个数字后会发生冲突(生日悖论),因此需要6位数字才能安全生成而不进行检查(旧版本仅生成4位数字,如果不进行检查,则在1300个ID之后会发生冲突) 。

如果进行冲突检查,位数可以减少3或4,但是请注意,当您生成越来越多的UID时,性能将线性降低。

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

如果需要唯一性而不是不可预测性,请考虑使用顺序生成器(例如user134_item1,,user134_item2…)。您可以“哈希”顺序生成的字符串以恢复不可预测性。

使用生成的UIDMath.random是不安全的(无论如何,您都不应该信任客户端)。难道不是依靠其在关键任务工作的独特性或不可预测性。


真好 很干净 您能否解释一下为什么要+ "1000"这么做?
Kranu 2011年

4
@Kranu-偶尔会生成随机数“ 0”或“ .000000000001”等,最后的字符串以“ 4z”结尾。在“0000”,确保它始终至少长4个字符
马克·卡恩

1
@RobG:因为OP想要一个“短”的UID。
kennytm 2011年

1
@RobG:如果只需要一个会话中的客户端唯一,以确保没有争用条件,那将很好地工作。
kennytm 2011年

1
对于较小的随机ID块,它可以工作++,简短而又好用,而无需下载外部库。当前用于为动态创建的HTML元素生成ID。
马克·卡彭特(Marc Carpenter Jr)'18年

30

更新08/2020:

shortid已被弃用,以支持更小更快的nanoid

  • 。108个字节(缩小并压缩)。没有依赖关系。大小限制控制大小。
  • 。它比UUID快40%。
  • 安全。它使用具有加密功能的随机API。可以在集群中使用。
  • 紧凑。它使用的字母比UUID(A-Za-z0-9_-)大。因此,ID大小从36个符号减少到21个。
  • 随身携带。Nano ID已移植到14种编程语言。
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"

更多信息在这里:https : //zelark.github.io/nano-id-cc/


旧答案

还有一个很棒的npm软件包:shortid

简短的非顺序url友好型唯一ID生成器。

ShortId创建惊人的短的非顺序url友好的唯一ID。非常适合网址缩短器,MongoDB和Redis ID,以及其他用户可能会看到的ID。

  • 预设为7-14个网址友好字元:AZ,az,0-9,_-
  • 非顺序的,因此它们是不可预测的。
  • 支持群集(自动),自定义种子,自定义字母。
  • 可以生成任意数量的ID(无重复),甚至每天数百万。
  • 非常适合游戏,尤其是如果您担心作弊,因此您不希望有容易猜到的ID。
  • 应用程序可以重新启动任何次数,而无需重复ID。
  • Mongo ID / Mongoose ID的流行替代品。
  • 可在Node,io.js和Web浏览器中使用。
  • 包括摩卡咖啡测试。

用法

var shortid = require('shortid');
console.log(shortid.generate()); //PPBqWA9

我认为这应该是公认的答案。另一种选择,这是远不一样好,当然是用空格代替连字符,replace(/[-]/g, ''),这下到的32的长度
这将是酷

2
非常不同意“下载可能有害的软件包”应该是编程问题的答案。
FredrikSchön19年

4
我也强烈不同意。开发人员来这里寻求代码答案和学习,而不管它们是否完美,都看不到NPM软件包的下载链接。对于我的用例,我不能使用任何软件包,而必须代码本身中集成解决方案。
vsync

4
不同意反对者!OP需要UID。OP没有表达您的其他约束。对于大多数人而言,经过良好测试的库比重新发明轮子要好,并且是正确的解决方案。如果您有其他问题,则可以发布其他问题或替代答案。
查尔斯·艾伦

@AjahnCharles但是您肯定会不同意应该接受的说法,您不会吗?这是一个有效的解决方案,正当其时,但这不是公认的答案。
Martin Braun

11

这是一个衬纸,但只给出小写字母和数字:

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

console.log(uuid);


5

如果区分大小写并且所有位置均允许数字,则以下代码将生成3个字符的62 ^ 3(238,328)个唯一值。如果不要求区分大小写,请从字符字符串中删除大写或小写字符,它将生成35 ^ 3(42,875)个唯一值。

可以很容易地进行修改,以便第一个字符始终是一个字母或所有字母。

不容置疑,但可以对其进行优化,并且在达到限制时也可以拒绝返回ID。

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

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

1
请说明拒绝正确而优雅的答案的原因,而不是拒绝其他类似答案的原因,谢谢
ninjagecko 2011年

2
我不是不好的人,但是我几乎愿意为with(Math)邪恶再给一个:)
Mark Kahn

@cwolves-我认为with在非性能代码中可能会毫无问题地使用它,并且“ withis EVIL”很容易被极端化。=)性能既不是因素(如果只是不使用它),也不是创建或破坏变量的问题(不进行赋值),也不是与全局变量混淆的问题。我宁愿对性能造成小的影响,也不必在全局范围内重新定义整个Math模块。
ninjagecko 2011年

3
@cwolves-实际上没关系,我只是意识到,如果一个with (Math)人定义一个变量,var max = ...那么它将覆盖Math.max.........好了,不再使用with
ninjagecko 2011年

1
我并不是很在意它的性能,而是它的细微差别,以及您必须搜索以前的代码行才能确定floorrandom实际引用的事实
Mark Kahn

2

这将生成一系列唯一值。当所有值都用尽时,它可以通过增加字符串长度来改善RobG的答案。

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

用法:

var g = new IdGenerator(),
    i;

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

该要点包含上述实现和递归版本。


2

得到一个简单的计数器,从100000000开始,将数字转换为基数36。

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

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

您可以轻松拥有20亿个优雅的独特ID,就像YouTube一样



0

只是随机生成一些字符串:

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

2
生成随机字符串然后进行测试以查看它们是否唯一似乎效率低下。生成唯一的字符串(其中的unique具有一定范围或上下文)非常简单,无论是否使用简单的计数器随机生成组件。
RobG 2011年

@RobG-如果要生成6位数字密钥(其中有568亿个唯一密钥),则其中一种不唯一的几率非常低。几乎不会发生碰撞,因此几乎不会再生。
Mark Kahn

1
@cwolves-为什么在可以简单避免的情况下留下任何机会?为什么要生成一个必须首先检查其唯一性的值,才能首先生成保证的唯一值?
RobG 2011年

@RobG-因为OP可能不需要“ 000”,“ 001”,...“ 00z”,“ 00A”,...,并且随机生成这些是解决该问题的简单方法。即使您要对它们进行哈希处理,您仍然需要哈希的基本冲突检测。同样,这些可能会在页面加载之间使用,等等,在这种情况下,您并不总是希望从1开始。由于OP不需要计数器,因此提供以62为底的计数器也没有用。
Mark Kahn

OP可以生成所需数量的ID并随机分配它们,以便它们不是顺序的(在问题中未指定,但也许是必需的)。
RobG 2011年

0

您可以使用md5算法生成随机字符串。md5是节点包

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

每次都会生成唯一的字符串。


@vsync这是正确的解决方案吗?Node的md5软件包。
Prathamesh更多
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.