🉑
TypeScriptの簡易ランダム英数文字列生成
TypeScript(JavaScript)でHTMLタグのid属性に使用できる簡易的なユニークな英数ランダム文字列生成機が欲しくなり実装した備忘メモです。
前提
- 英数文字列で構成される任意長の文字列を生成する
- ただし最初の文字は数字にしない
- 大文字と小文字は区別する
- 実装の簡単化のため
z|Z
は含まない - 文字列長は指定可能にする
- 必要に応じてユニーク保証回数の制限を設定可能にする
実装
code
class HtmlIds {
#limit = Number.POSITIVE_INFINITY;
#length;
#store = new Set<string>();
constructor(len: number, limit: number = 0) {
if (limit > 0) { this.#limit = limit; }
this.#length = len;
}
get(): string {
if (this.#store.size > this.#limit) { this.#store.clear(); }
let id = HtmlIds.#gen(this.#length);
while (this.#store.has(id)) { id = HtmlIds.#gen(this.#length); }
this.#store.add(id);
return id;
}
static #gen(len: number): string {
function getRandChar(nodigit: boolean) {
const ALPHABETIC = [[65, 25],[97, 25]]; // [A-Y],[a-y]
const ALPHANUMERIC = [...ALPHABETIC, [48, 10]]; // [A-Y],[a-y],[0-9]
const baseAry = nodigit ? ALPHABETIC : ALPHANUMERIC;
const [base, rate] = baseAry[Math.trunc(Math.random() * baseAry.length)];
return Math.trunc(base + (Math.random() * rate));
}
return String.fromCharCode(...Array(len).fill(null).map((_,i) => getRandChar(!i)));
}
}
使い方
- 生成長と必要に応じて回数制限を指定しインスタンス化する
const genId = new HtmlIds(4);
const genIdWithLimit = new HtmlIds(5, 2000);
- 必要な時に取得する
console.log(genId.get());
// "jKub"
console.log(genIdWithLimit.get());
// "CB87n"
簡単な解説
get(): string {
if (this.#store.size > this.#limit) { this.#store.clear(); }
let id = HtmlIds.#gen(this.#length);
while (this.#store.has(id)) { id = HtmlIds.#gen(this.#length); }
this.#store.add(id);
return id;
}
-
this.#store
(Set
)を用いてユニークを担保 - 結果重複時は再生成する
- 生成数が制限を超えた場合
Set
をリセット
static #gen(len: number): string {
function getRandChar(nodigit: boolean) {
const ALPHABETIC = [[65, 25],[97, 25]]; // [A-Y],[a-y]
const ALPHANUMERIC = [...ALPHABETIC, [48, 10]]; // [A-Y],[a-y],[0-9]
const baseAry = nodigit ? ALPHABETIC : ALPHANUMERIC;
const [base, rate] = baseAry[Math.trunc(Math.random() * baseAry.length)];
return Math.trunc(base + (Math.random() * rate));
}
return String.fromCharCode(...Array(len).fill(null).map((_,i) => getRandChar(!i)));
}
-
baseAry
でMath.random
を用いて文字2種or3種の中のどれにするか選択 - 文字種の最初のasciiコードから
Math.random
でオフセットを計算 -
String.fromCharCode
でasciiコードを文字列化- 1文字目(インデックス0)は
nodigit
がtrue
になる
- 1文字目(インデックス0)は
- なぜ
z
を含まないか- 26文字にするとこの方法では確率に偏りが出るため
- 手動設定時でも
z
を含めると確実にユニークになるため
- 組み合わせ数参考
文字数 | 組み合わせ数 |
---|---|
3 | 180,000通り (50*60*60) |
4 | 10,800,000通り (50*60*60*60) |
5 | 648,000,000通り (50*60*60*60*60) |
6 | 38,880,000,000通り (50*60*60*60*60*60) |
おまけ
UUID生成
function genUUIDv4(): string {
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => (+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16));
}
console.log(genUUIDv4());
// "606c8fc6-a04e-46df-9935-8ebb1d991bca"
console.log(crypto.randomUUID());
// "c9d6fd0f-ffbe-49d9-8c60-554b5e9e575e"
Discussion