Counting and JSON output in Jekyll

Jekyll has a built-in filter called jsonify to turn data into JSON and Liquid has a filter called json. These is pretty useful for turning standard Jekyll output arrays into JSON, but if you need something more customized, you’ll need to build it yourself because Liquid’s ability to create arrays is limited. (Not a criticism, just an observation. It is primarily a templating language.)

For example, I wanted to store the dates I have blog posts for and the number of posts on each of those dates in order to pass it to a heatmap calendar. I couldn’t figure out a way to build that into a single array with Liquid (and I didn’t want to drop down into Ruby), so I figured out a way build JSON manually.

This outputs my blog’s post dates in the Unix timestamp format and displays the number of posts on a given day and leaves the trailing comma off to comply with the JSON standard:

{% assign counter = 0 %}{ {% for post in site.posts %}{% capture day %}{{ post.date | date: '%s' }}{% endcapture %}{% capture prevday %}{{ post.previous.date | date: '%s' }}{% endcapture %}{% assign counter = counter | plus: 1 %}{% if day != prevday %}"{{ post.date | date: '%s' }}": {{ counter }}{% assign counter = 0 %}{% if forloop.last == false %},{% endif %} {% endif %}{% endfor %}}

Output:

{ "1463716800": 1, "1463371200": 1, "1462593600": 2, "1462507200": 1, "1462248000": 1, "1462161600": 1, "1461643200": 1, "1461297600": 3, "1461124800": 1, "1460952000": 1, "1460606400": 1, "1460347200": 1, "1459915200": 1, "1458792000": 1, "1458705600": 1, "1458619200": 1, "1458532800": 1, "1458446400": 1, "1458273600": 2, "1457586000": 1, "1457499600": 1, "1457413200": 1, "1457326800": 1, "1457240400": 1, "1456981200": 1, "1456894800": 1, "1456808400": 1, "1456722000": 1, "1456376400": 1, "1456290000": 1, "1456203600": 1, "1456117200": 1, "1456030800": 1, "1455944400": 1, "1455858000": 1, "1455771600": 2, "1455685200": 1, "1455598800": 1, "1455512400": 1, "1455339600": 1, "1455253200": 1, "1455166800": 1, "1455080400": 1, "1449032400": 1, "1441944000": 1, "1441598400": 1, "1441166400": 1, "1416978000": 1, "1377662400": 1 }

The key to getting the counts right was advancing the counter by 1, comparing the date of the current post to the previous post, outputting the date and the count if they did not match, and if they did match advancing the counter by 1 and comparing again. After the date and count are output, the counter is reset to 0.

You can see the heatmap calendar on my TIL page.

Thanks to Eric Davis for advice on using less code and pointing out how I can make the trailing comma if statement more efficient: %if forloop.last == false% , %endif%

He said:

If there’s one bit of “programming advice” I would give to anybody mucking around with this stuff, it’s this: Write as little code as humanly possible. The most bug-free line of code is the line not written.

What I need to ask myself: If something is being repeated, why?