【问题标题】:How to generate short uid like "aX4j9Z" (in JS)如何生成像“aX4j9Z”这样的短uid(在JS中)
【发布时间】:2011-09-09 01:43:24
【问题描述】:

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

我想为我的 uids (guids) 提供类似“aX4j9Z”的东西。

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

【问题讨论】:

  • 什么是“uid”和“guid”?生成唯一值的最简单方法是以“x”之类的字符串开头,然后附加一个由计数器生成的数字,这样就可以得到“x0”、“x1”等。 “独特”的上下文是什么?元素 ID 和名称?某个对象的属性?其他?
  • 只需放置一个关于此主题的 GISTS 搜索链接:gist.github.com/search?l=JavaScript&q=unique+id

标签: javascript hash guid


【解决方案1】:

有关预打包的解决方案(shortid package),请参阅 @Mohamed's answer。如果您没有特殊要求,请优先使用此页面而不是此页面上的任何其他解决方案。


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

随机生成的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_item1user134_item2、...)。您可以“散列”顺序生成的字符串以恢复不可预测性。

使用Math.random 生成的UID 并不安全(而且您也不应该信任客户端)。 不要在关键任务任务中依赖其独特性或不可预测性。

【讨论】:

  • 不错。那很干净。你能解释一下为什么最后你+ "1000"吗?
  • @Kranu - 如果它生成随机数“0”或“.000000000001”等,最终字符串最终会像“4z”。 “0000”确保它始终至少有 4 个字符长
  • @RobG:因为 OP 想要一个“短”的 UID。
  • @RobG:如果它只需要在一个会话中对客户端是唯一的,以确保没有竞争条件,那效果很好。
  • 对于随机 ID 的小块,这可以 ++,简短而甜蜜,无需下载外部库。目前用于为动态创建的 HTML 元素生成 ID。
【解决方案2】:

2020 年 8 月更新:

shortid 已被弃用,取而代之的是更小更快的nanoid

  • 。 108 字节(压缩和压缩)。没有依赖关系。 Size Limit 控制大小。
  • 快速。它比 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。非常适合 url 缩短器、MongoDB 和 Redis id 以及任何其他用户可能看到的 id。

  • 默认 7-14 个 url 友好字符:A-Z、a-z、0-9、_-
  • 不连续,因此不可预测。
  • 支持集群(自动)、自定义种子、自定义字母表。
  • 可以生成任意数量的不重复的 ID,甚至每天数百万。
  • 非常适合游戏,尤其是如果您担心作弊,因此您不想要一个容易猜到的 ID。
  • 应用程序可以重新启动任意次数,没有任何重复 id 的机会。
  • Mongo ID/Mongoose ID 的流行替代品。
  • 适用于 Node、io.js 和 Web 浏览器。
  • 包括 Mocha 测试。

用法

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

【讨论】:

  • 我认为这应该是公认的答案。另一个远没有那么好的选择,当然是将连字符替换为空格,replace(/[-]/g, ''),它的长度可以缩短到 32。
  • 非常不同意“下载可能有害的包”应该是编程问题的答案。
  • 我也强烈不同意。开发人员来这里是为了code 答案和学习,而不是为了查看 NPM 包的下载链接,无论它们是否完美。对于我的用例,我不能使用任何包,并且必须将解决方案 集成到代码本身中。
  • 不同意不同意的人! OP 想要 UID。 OP 没有表达您的额外约束。对于大多数人来说,一个经过良好测试的库比重新发明轮子更好,并且是正确的解决方案。如果您有其他问题,可以发布其他问题或替代答案。
  • @AjahnCharles 但是你肯定会不同意这应该是公认的答案的说法,你不会吗?这是一个有效的解决方案,它就在这里,但它不是一个被接受的答案,这是正确的。
【解决方案3】:

这是一个单行,但它只给出小写字母和数字:

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

console.log(uuid);

【讨论】:

  • 可以输入Date.now() 得到一个有某种意义的序列:Math.floor(Date.now() / 1000).toString(36);
  • @Campbeln 请注意,这会导致大量重复。
【解决方案4】:

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

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

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

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

【讨论】:

  • 我觉得这很有帮助;很高兴不必为此下载整个包或添加功能。我使用了自纪元以来的毫秒数:(Math.round(Date.now())).toString(36)
【解决方案5】:

以下生成 62^3 (238,328) 个 3 个字符的唯一值,前提是区分大小写是唯一的,并且所有位置都允许使用数字。如果需要不区分大小写,请从 chars 字符串中删除大写或小写字符,它将生成 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;
  }
}());

【讨论】:

    【解决方案6】:
    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;
    }
    

    【讨论】:

    • 请解释拒绝正确而优雅的答案的原因,同时不要拒绝其他类似的答案,谢谢
    • 我没有投反对票,但我几乎愿意为 with(Math) 邪恶投另一个票:)
    • @cwolves - 我认为with 有可能在非性能代码中毫无问题地使用,并且“with 是邪恶的”很容易被带到极端。 =) 性能也不是一个因素(如果只是不使用它),创建或破坏变量也不是问题(不进行分配),与全局变量混淆也不是问题。我宁愿在性能上受到一点影响,也不愿在全局范围内重新定义整个数学模块。
    • @cwolves - 其实没关系,我刚刚意识到,如果一个人做了with (Math),一个人定义了一个变量var max = ...,那么一个人将覆盖Math.max......好吧不不再使用with
    • 我并不真正关心它的性能,更多的是它的细微差别,事实上你必须去搜索前面的代码行才能确定 floor 和 @ 987654330@其实是指
    【解决方案7】:

    这将生成一系列唯一值。它通过在用尽所有值时增加字符串长度来改进 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());
    }
    

    This gist 包含上述实现和递归版本。

    【讨论】:

      【解决方案8】:

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

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

      【讨论】:

      • 生成随机字符串似乎效率低下,然后必须测试它们是否唯一。生成唯一字符串非常简单(其中唯一的有一些范围或上下文),无论有没有使用简单计数器随机生成的组件。
      • @RobG - 如果您正在生成 6 位密钥(有 568 亿个唯一密钥),其中一个非唯一密钥的几率非常低。几乎永远不会发生碰撞,因此几乎永远不会重新生成。
      • @cwolves - 当它可以被简单地避免时,为什么要留下 任何 机会?当您可以首先生成保证唯一值时,为什么还要生成一个必须检查其唯一性的值?
      • @RobG - 因为 OP 可能不想要“000”、“001”、...“00z”、“00A”,......并且随机生成这些是解决这个问题的简单方法。即使您要散列它们,您仍然需要对散列进行基本的冲突检测。此外,这些可能会在页面加载等之间使用,在这种情况下,您并不总是希望从 1 开始。我的一般论点是,如果 OP 只想要页面的 guid,一个简单的计数器就可以正常工作。由于 OP 没有要求计数器,因此提供 base-62 计数器也没那么有用。
      • OP 可以生成所需数量的 ID 并随机分配它们,因此它们不是连续的(问题中未指定,但可能需要)。
      【解决方案9】:

      您可以将 GUID 缩短为 20 个可打印的 ASCII 字符,而不会丢失信息或 GUID 的唯一性。

      Jeff Atwood 多年前曾在博客中提到过:
      Equipping our ASCII Armor

      【讨论】:

        【解决方案10】:

        您可以使用 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 包。
        【解决方案11】:

        此解决方案将Math.random() 与计数器结合在一起。

        Math.random() 应该提供大约 53 位的熵(与 UUIDv4 的 128 位相比),但是当与计数器结合使用时,应该为 临时 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()))

        特点:

        • 简单的实现
        • 输出大约 13 个字符
        • 不区分大小写
        • 可安全用作 HTML id 和 React key
        • 不适合数据库存储

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-10
          • 1970-01-01
          • 1970-01-01
          • 2012-08-11
          • 2016-10-13
          • 2011-02-17
          • 1970-01-01
          • 2016-07-20
          相关资源
          最近更新 更多