Skip to content
back to top
Just the code, please

Shailesh Codes

TO BUILD A BLOG

Adding estimated reading times to my blog posts

By Shailesh Vasandani — Last updated on January 02, 2021 — Reading time: 4 min

to build a blog java software
More from TO BUILD A BLOG
1

I built a website with a blog about... building a website?

I'm starting a blog, and I really don't know what to put in it.

2

Tag, you're it — writing a JavaScript tagging library

You see these tags? If not, that's awkward, but if you do, you have this JavaScript library to thank.

3

Blogs that show an estimated reading time can get upwards of 400% increased click-through rates. Since I roll my own stack for my statically built blog posts, I thought it would be a fun exercise to try and add this useful metric to my blog page.

The stack

In general, HTML templaters are designed to take custom data and merge them with predefined templates. My current blog post templating setup is a single Java program which uses almost Markdown-like syntax to insert content into a prewritten HTML file. I won't go into the details too much, but suffice it to say that it regenerates both the blog posts and any internal links — for example, the "Recommended Posts" section at the bottom.

Aside from each blog post, it also generates a JSON file that my blog page taps into to show the post previews. By doing this, I can effectively have a purely static site that is as responsive as something like a WordPress site. Overall, the stack consists of Java as the site generator, and HTML, CSS, and JavaScript as the frontend technologies.

Getting the actual numbers

Calculating reading time is supposedly quite simple — just take the number of words in your article, and divide by the average reading speed of approximately 265 words. When it comes to blogs with code, however, this number can change dramatically. If the code is very dense, even a few words can take a minute or two. Excessively verbose code, however, can just be skimmed over. I decided that I would simply count the code by line, assigning the equivalent of about 1 line per word.

That may seem very little, but remember that code often has lots of lines containing no more than a single character. It's probably also helpful that I try to write my code to be readable and atomic; that is, each line should be short, sweet, and understandable. To ensure that I didn't underestimate reading time by too much, I decided to reduce the target speed from 265 to 200. I also did it because the number seemed a lot nicer. Why not 250, you ask? I don't know. Arbitrary numbers in hand, the formula to calculate reading time is pretty simple:

        
      readingTime = (numWords + linesOfCode) / 200;

Code is never so simple, is it

While it would be super useful to simply plop that formula in the Java file somewhere and have everything work, that would be far too easy. You see, I wrote my Java templating program with the goal of extracting repeated code and making functions testable. To this end, I have lots of functions that each do one thing and then call other functions, passing along any necessary variables as parameters and eventually returning the template String that becomes the final product.

Since Java can only return one value (excluding using arrays to return multiple), any other information that needs to be returned needs to be passed in as an argument and changed inside the inner function. However, ints in Java are passed-by-value (well technically everything is, but we conveniently ignore that). This means that I can't simply pass in a pointer to a wordCount variable and update it inside the inner function, like I might be able to in C. So we're stuck in a rut — how do we get out?

Enter the hackiest solution I've probably ever done so far. Instead of passing an int into a function, I'm passing in an int array of length 1. Java passes this reference along, and I'm able to change the value of the first element in this single element array, bubbling up this value to the outer function. Because I have about three or so layers of nesting, my functions look a little like this:

Hey, it looks like you're on a slightly narrower screen. The code blocks below might not look too good, but the rest of the article should be fine. You can hop on a wider screen if you want to follow along. I'm not going anywhere (promise).

        
      public String buildHTML(String filename) {
        // ...
        int[] wordCount = new int[1]; // pass by "reference"
        String content = buildContent(fileScanner, wordCount);

        readingTime = wordCount[0] / 200;

        // ...
      }

      public String buildContent(Scanner fileScanner, int[] wordCount) {
        // ...
        else if (lineArray[0].toUpperCase().equals("P")) {
          content += buildParagraph(lineArray[1], wordCount);
        } else if (lineArray[0].toUpperCase().equals("CODE")) {
           content += buildCode(fileScanner, wordCount);
        }

        // ...
      }

      public String buildParagraph(String pData, int[] wordCount) {
        // ...
        wordCount[0] += content.split(" ").length; // actually setting the data

        //...
      }

      public String buildCode(Scanner fileScanner, int[] wordCount) {
        // ...
        while (fileScanner.hasNextLine()) {
          wordCount[0]++; // one "word" per line

          // ...
        }

        // ...
      }

As you can see, we're successfully able to pass that value down the chain and read it when it comes back. Not bad for a pass-by-value language like Java. To see the end result, take a look at my blog page and look at the top of any of the posts.

I hope that hearing about how I added this feature helped inspire you to tackle some annoying bug or implement an interesting feature idea you have.

As always, don't forget to follow me for more content like this. I'm currently writing on dev.to and Medium, and your support on either platform would be very much appreciated. I also have a membership set up, where you can get early previews of articles and exclusive access to a whole bunch of resources. Also, if you've particularly enjoyed this post, consider supporting me by buying me a coffee. Until next time!

Subscribe to my mailing list!

Hey there! Subscribe to my mailing list to get monthly updates on my most useful posts, and any special announcements I have. No spam, ever — promise. All fields are required.
Recommended for you

Low risk, high reward

An intro to site reliability engineering

CPR for your app: some tricks to try

What to do when your app just isn't running.

How to store data with IndexedDB

It's well supported, allows you to store large files, and isn't even that bad to work with.

Real-life examples of JavaScript's filter function

The third and final part of a series on JavaScript's array functions.

Comments

Write a comment!

All comments are moderated, and will only be published if they're constructive and add to the discussion. Thanks in advance! All fields are required.

copy