Some Ruby on Rails side-project hacking I’ve been doing led me to need to generate shortened URLs. The ShortURL gem is fine if you want to use TinyURL, Snurl, or some other external service to generate and manage your URLs, but in my case, I need to host the URLs so I can track usage statistics and redirect to a URL determined at request-time.
I wanted to avoid generating curse words, or any words for that matter, so I opted not to use vowels rather than try and figure out something more clever. I also had no requirement to obfuscate the sequential nature of the generated value. The result essentially converts a number to a base 54 string suitable for use as a URL parameter.
URL_CHARS = ('0'..'9').to_a + %w(b c d f g h j k l m n p q r s t v w x y z) + %w(B C D F G H J K L M N P Q R S T V W X Y Z - _)
URL_BASE = URL_CHARS.size
def generateUrl idNumber
localCount = idNumber
result = ”;
while localCount != 0
rem = localCount % URL_BASE
localCount = (localCount – rem) / URL_BASE
result = URL_CHARS[rem] + result
end
return result
end
My usage will be to convert the numeric primary key of my model objects to this hash, so that Person #1174229 could be referenced as the short url http://app.com/p?i=7sH_
Obviously, the straightforward business of inserting this value into your table, performing lookups on it, etc is omitted and should be fairly straightforward to a seasoned developer. The function can fit almost a half-billion values into 5 characters, and doesn’t experience the stack overflow issues of some recursive implementations I found, so I think it’s pretty useful. (7 characters give you 1.3 trillion possibilities… nice!)
I can see a pretty cool plugin being built to solve this problem… maybe I’ll get around to writing short_url_fu one of these days.
this is sweet — i have thought about something like this before … definitely will use this
Glad it’s useful for you. That’s the whole point of sharing it. 🙂
This is nice. How about code to go the other way around, so you don’t have to store the url value in your table and can decode it on the fly?
is it a perfect hash?
This line:
result = URL_CHARS[rem] + result
Generates a reverse result…
Shouldn’t it be:
result = result + URL_CHARS[rem]
And get “_Hs7” as the result?
Regards
@MarcRic I don’t think so. Each iteration, this code is calculating the remainder, then dividing by the size of the array. Thus, each successive iteration of the loop is of increasing significance and should be prepended. Check it with smaller numbers and see if your hand calculation disagrees with the algorithm result.