LiquidBase60

Off the back of the Tantek Çelik’s enlighting and superb work on Whistle1 and NewBase602, I yearned for a way to generate short-URLs for each of my post categories, and figured out a way, albeit a bit over-complicated, using vanilla Jekyll/Liquid.

For example, there is a Note that I published on 08 September 2018, and is the 1st (and only) Note of that day. Let’s look at the page’s short-URL and break down what the code at the end means:

repc.co/n4wN1

This code is comprised of three parts:

  1. nCategory (required, 1 character)
  2. 4wNSexagesimal Epoch Days (required, 3 characters)
  3. 1Post Number for the Day (optional, 1 character, default = 1)

Category

Firstly, we need to figure out the category for a particular page. I’m assigning a letter to each category for two reasons: to limit the loop to posts of that type, and to make it such that one can discern the category from the short-URL’s slug.

Category Prefix
Article a
Bookmark h
Note n
{%- if page.category == 'article' -%}
    a
{%- elsif page.category == 'bookmark' -%}
    h
{%- elsif page.category == 'note' -%}
    n
{%- endif -%}

Sexagesimal Epoch Days

How do we calculate Epoch days from a given post’s date?

The full ISO 8601 datetime for our example post is 2018-09-08T23:58:42+01:00, and the first step is to convert this to a Unix timestamp (seconds since Epoch), and from there we can divide by 86400 (the number of seconds in a day) to get the number of days since Epoch. Fortunately, this is quite trivial with Jekyll:

2018-09-08T23:58:42+01:00153644752217782 (rounded down)

{% assign n = page.date | date: '%s' | divided_by: 86400 | floor %}

Sexagesimal is a number system with 60 as its base and is actually over 5000 years old3! For our short-URLs, the 60 characters are represented by the following:

0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz

In order to complete this step, we need to loop through our Epoch days tallying and converting into base 60 until we hit 0. One limitation that we’ll experience with using Liquid (as opposed to NewBase60’s original languages, PHP or JavaScript) is its lack of a while loop, so the following won’t work:

{% while condition != satisfied %}
    ...
{% endwhile %}

However, we can mimic one using a for loop that counts to a (theoretically) unreachable number, and we can break out of the loop when our condition is met (as with a standard while loop):

{% for i in (1..9999) %}
    ...
    {% if condition == satisfied %}
        {% break %}
    {% endif %}
{% endfor %}

Much like a while loop, we are not concerned with the iteration variable, i, we simply want to perform an action until a condition is met—it doesn’t matter how many iterations are required.

Putting it all together, our code now looks like the following, where we are storing our output in the s variable:

{% assign n = page.date | date: '%s' | divided_by: 86400 | floor %}
{% assign s = "" %}
{% assign m = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz" | split: "" %}
{% for i in (1..9999) %}
    {% assign d = n | modulo: 60 %}
    {% assign s = s | prepend: m[d] %}
    {% assign n = n | minus: d | divided_by: 60 %}
    {% if n <= 0 %}
        {% break %}
    {% endif %}
{% endfor %}
{{ s }}

Post Number for the Day

The remaining portion of work is to determine how many posts of the given category have been published on the given day, and which of those posts, chronologically, this one is. To do so, we need to add an additional action to our category checks which instructs which category of posts to trawl:

{%- if page.category == 'article' -%}
    a
    {%- assign posts_to_check = site.categories.article -%}
{%- elsif page.category == 'bookmark' -%}
    h
    {%- assign posts_to_check = site.categories.bookmark -%}
{%- elsif page.category == 'note' -%}
    n
    {%- assign posts_to_check = site.categories.note -%}
{%- endif -%}

We can use this categorised list of posts to match dates against and build an array of posts under the specific category on the specific day. Looping through this array of posts until we reach the post for which we’re building and pulling out the array index gives us the number we’re looking for:

{%- assign page_date = page.date | date: '%Y-%m-%d' -%}
{%- assign filtered_posts = site.emptyArray -%}
{%- for check in posts_to_check -%}
    {%- assign check_date = check.date | date: '%Y-%m-%d' -%}
    {%- if check_date == page_date -%}
        {%- assign filtered_posts = filtered_posts | push: check -%}
    {%- endif -%}
{%- endfor -%}
{%- for check in filtered_posts -%}
    {%- if check.title == page.title -%}
        {%- assign shorturl = shorturl | append: forloop.index -%}
        {%- break -%}
    {%- endif -%}
{%- endfor -%}

Presenting…

{%- capture shorturl -%}
    {%- if page.category == 'article' -%}
        a
        {%- assign posts_to_check = site.categories.article -%}
    {%- elsif page.category == 'bookmark' -%}
        h
        {%- assign posts_to_check = site.categories.bookmark -%}
    {%- elsif page.category == 'note' -%}
        n
        {%- assign posts_to_check = site.categories.note -%}
    {%- endif -%}
    {%- assign n = page.date | date: '%s' | divided_by: 86400 -%}
    {%- assign s = "" -%}
    {%- assign m = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz" | split: "" -%}
    {%- if n == empty or n == 0 -%}0{%- endif -%}
    {%- for i in (1..9999) -%}
        {%- assign d = n | modulo: 60 -%}
        {%- assign s = m[d] | append: s -%}
        {%- assign n = n | minus: d | divided_by: 60 -%}
        {%- if n <= 0 -%}
            {%- break -%}
        {%- endif -%}
    {%- endfor -%}{{ s }}
{%- endcapture -%}
{%- assign page_date = page.date | date: '%Y-%m-%d' -%}
{%- assign filtered_posts = site.emptyArray -%}
{%- for check in posts_to_check -%}
    {%- assign check_date = check.date | date: '%Y-%m-%d' -%}
    {%- if check_date == page_date -%}
        {%- assign filtered_posts = filtered_posts | push: check -%}
    {%- endif -%}
{%- endfor -%}
{%- for check in filtered_posts -%}
    {%- if check.title == page.title -%}
        {%- assign shorturl = shorturl | append: forloop.index -%}
        {%- break -%}
    {%- endif -%}
{%- endfor -%}

At last, the following variable becomes available for use:

{{ shorturl }}
🗓 published on
📚 reading length ~550 words
🏷 tagged under ,
🔗 shorturl repc.co/a4zK2


Sparkline Sound-Off Like from 04 March 2019