さまざまな場面でIDを生成したくなることがあると思います。Qiitaの記事のURLも何らかのアルゴリズムでIDが生成されています。例、https://qiita.com/mnishiguchi/items/90380b61df4e41966556
ID生成のやり方と言っても要求事項によるので銀の弾丸はないと思います。
いろんなパターンを知っておくといざという時の判断材料になると思い、お手本があればメモするようにしています。
日付・時間
- 単純なデータなので処理が高速
- 厳密にいうとIDではないがこれで用が足りることもある
System.monotonic_time/1
- 不特定の時点から始まる単調に増加する時間
- 例、
50549
System.monotonic_time(:millisecond)
System.os_time/1
- オペレーティングシステムの時間
- 例、
1668560784473
System.os_time(:millisecond)
DateTime.to_unix/2
-
System.os_time(:second)
と同じっぽい - 例、
1668560794
DateTime.utc_now |> DateTime.to_unix
DateTime.to_iso8601/3
- 例、
"2022-11-16T01:06:51.322281Z"
DateTime.utc_now |> DateTime.to_iso8601
文字列のMD5ハッシュ値を計算しBase16にエンコード
- 不可解な値に見せたいときに最適
- 例、
"E3FABC6EBCAA52300311DF162AE35BF1"
IEx
iex> "闘魂" |> :erlang.md5() |> Base.encode16()
"E3FABC6EBCAA52300311DF162AE35BF1"
IEx
iex> "闘魂" |> :erlang.md5() |> Base.encode16(case: :lower)
"e3fabc6ebcaa52300311df162ae35bf1"
:crypto.strong_rand_bytes/1
- 衝突を避けたいときに最適
- LivebookでランダムIDの生成に使用されている
- PhoenixのMix.Tasks.Phx.Gen.Secretで使用されている
Livebookの例
# random_short_id
# 例、"kmrfwizk"
:crypto.strong_rand_bytes(5) |> Base.encode32(case: :lower)
# random_id
# 例、"3nbztnwq5wi6fhmerpgxbwbryo6b27os"
:crypto.strong_rand_bytes(20) |> Base.encode32(case: :lower)
# random_cookie
# 例、"5QpirBHr0XWdCCdudCsvZ-nuNLuiRAkF0wR_DdKm03plKbwEvvSG"
:crypto.strong_rand_bytes(39) |> Base.url_encode64()
文字列の長さを一定にしたい場合は、Kernel.binary_slice/2 (または Kernel.binary_part/3)が便利そうです。
:crypto.strong_rand_bytes(20) |> Base.url_encode64 |> binary_part(0, 20)
:crypto.strong_rand_bytes(20) |> Base.url_encode64 |> binary_slice(0..19)
Mix.Tasks.Phx.Gen.Secretの例
- ランダム文字列の長さの最小値が32となっている
- Kernel.binary_part/3を用いて結果をユーザー指定の長さに調整している
defmodule MySecret do
def random_string(length) when length > 31 do
:crypto.strong_rand_bytes(length)
|> Base.encode64(padding: false)
|> binary_part(0, length)
end
def random_string(_) do
Mix.raise "The secret should be at least 32 characters long"
end
end
MySecret.random_string(32)
UUID
- 同一の文字列が2つ生成される可能性が高い大規模なコンテキストに最適
- zyro/elixir-uuidパッケージをインストールする必要がある
- 例、
69134ff3-4bfe-3945-a04d-7e694c77121c
UUIDのバージョン
- バージョン1 - MAC address + datetime into 128 bits
- バージョン3 - MD5 hash into 128 bits
- バージョン4 - random data into 128 bits
- バージョン5 - SHA1 hash into 128 bits
迷ったらとりあえずバージョン3を選択すると無難そう。
zyro/elixir-uuidパッケージをインストールします。
IEx
iex> Mix.install [{:elixir_uuid, "~> 1.2.0"}]
名前空間を指定する必要がある。迷ったら:dns
にしておけばOK。
IEx
iex> UUID.uuid3(:dns, "mnishiguhchi.com")
"0aaa56b1-0810-3a73-aa3d-63a2f6b3d28d"
有効なUUIDを名前空間として使用することも可能。
IEx
iex> UUID.uuid3("0aaa56b1-0810-3a73-aa3d-63a2f6b3d28d", "闘魂")
"16c19ad5-e030-3829-9599-26961d8ef802"
ご参考までに