I'd like to use a UUID for database records but if I'm using it for the URL I'd like it to be 5 to 8 characters.
I know I need to use SecureRandom
and base64
, but how do I specify the length I need?
I'd like to use a UUID for database records but if I'm using it for the URL I'd like it to be 5 to 8 characters.
I know I need to use SecureRandom
and base64
, but how do I specify the length I need?
You can't get a real UUID down to 5-8 characters, as another answer points out, but you can shorten them somewhat. UUIDs are 128-bit integers which works out to 32 hex digits. You can easily store 6 bits per character and cut the length down to 22 characters, which is what base 64 encoding is. Standard base 64 encoding uses upper and lower case letters, digits, and "+" and "/" to finish it out. If you replace "+" and "/" with "-" and "_" you will end up with a string that doesn't have to be url encoded. You can do it like this (using UUIDTools to create the UUID):
uuid = UUIDTools::UUID.random_create
str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)
To get your value back out:
(str + "==\n").tr('-_','+/').unpack('m*').first if str =~ /^[a-zA-Z0-9_\-]{22}$/
That's assuming the UUID can be put into a raw format where it's a string of 16 8-bit characters. Here's an irb session showing a real example:
2.1.1 :016 > uuid=UUIDTools::UUID.random_create
=> #<UUID:0x83f1e98c UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627>
2.1.1 :017 > uuid.raw
=> " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'"
2.1.1 :018 > str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)
=> "INB7bFKvTlOv6ms60NDGJw"
2.1.1 :019 > uuid2 = (str + "==\n").tr('-_','+/').unpack('m*').first
=> " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'"
2.1.1 :022 > UUIDTools::UUID.parse_raw(uuid2)
=> #<UUID:0x849e6b44 UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627>
I use this method on various web sites where I typically use Postgres to generate UUIDs as primary keys for tables and pass them as ids. It doesn't save a lot of space but it does make some URLs fit on one 80 character line where a full UUID in standard format wouldn't. With dashes, a standard UUID is 36 characters so 22 is about 2/3 the size.
I created two single line function based on Michael Chaney
def encode(uuid)
[uuid.tr('-', '').scan(/../).map(&:hex).pack('c*')].pack('m*').tr('+/', '-_').slice(0..21)
end
def decode(short_id)
(short_id.tr('-_', '+/') + '==').unpack('m0').first.unpack('H8H4H4H4H12').join('-')
end
uuid = "355bf501-ffea-4f5a-a9e2-16074de6fcf2"
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2"
encode(uuid)
=> "NVv1Af_qT1qp4hYHTeb88g"
decode(_)
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2
I use an alphabet of 62 (Base62) characters, because I don't want -
and _
in my shortened UUIDs. The UUID shortener shortens a 36 long UUID to a 22 character long string. I use this UUID shortener in Rails. Change the alphabet to your needs.
Here my UUID shortener:
# lib/uuid_shortener.rb
module UuidShortener
ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
ALPHABET_HASH = ALPHABET.each_char.with_index.each_with_object({}) { |(k, v), h| h[k] = v; }
BASE = ALPHABET.length
class << self
def shorten(uuid)
num = uuid.tr('-', '').to_i(16)
return '0' if num.zero?
return nil if num.negative?
str = ''
while num.positive?
str = ALPHABET[num % BASE] + str
num /= BASE
end
str
end
def expand(suid)
num = i = 0
len = suid.length - 1
while i < suid.length
pow = BASE**(len - i)
num += ALPHABET_HASH[suid[i]] * pow
i += 1
end
num.to_s(16).rjust(32, '0').unpack('A8A4A4A4A12').join('-')
end
end
end
> uuid = SecureRandom.uuid
> uuid
=> "2ff7b050-2a37-4d52-a8f0-76cffccbefe3"
> suid = UuidShortener.shorten(uuid)
> suid
=> "1svPFI0god7vT7MNxKIrfR"
> UuidShortener.expand(suid)
=> "2ff7b050-2a37-4d52-a8f0-76cffccbefe3"
I know it's not exactly what you're looking for, but I like an open-source library that lets you generate short unique identifiers from numbers.
https://sqids.org (Previously HashID's)
Features
© 2022 - 2025 — McMap. All rights reserved.