Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you sort site.tags by post count in Jekyll?

Tags:

ruby

jekyll

Apologies as I'm new to Ruby, but I'm trying to add a liquid tag to my template that I can loop over to show a list of the five most popular tags.

For some reason this plugin just outputs a single tag when I use it.

Here is what I put in mu plugin:

module Jekyll
  class PopularTags < Liquid::Tag

    def initialize(tag_name, text, tokens)
      super
    end

    def render(context)
      tags = context.registers[:site].tags
      return tags.sort_by { |tag, posts| posts.count }
    end
  end
end
Liquid::Template.register_tag('popular_tags', Jekyll::PopularTags)

Here is what I put in my template:

{% popular_tags %}
like image 281
Justin Avatar asked Jul 11 '14 14:07

Justin


1 Answers

It's also possible to do this without plugins, which means that it will work on GitHub Pages.

I'm already doing something similar (without plugins as well) on my blog, where I'm displaying a list of tags with post counts, alphabetically sorted. The source code is here.

It's not much effort to modify this so that it sorts by post count:

{% capture tags %}
  {% for tag in site.tags %}
    {{ tag[1].size | plus: 1000 }}#{{ tag[0] }}#{{ tag[1].size }}
  {% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags reversed %}
    {% assign tagitems = tag | split: '#' %}
    <li><a href="/tags/#{{ tagitems[1] }}">{{ tagitems[1] }} ({{ tagitems[2] }})</a></li>
{% endfor %}

I guess there's some explanation necessary:

tag[0] is the name of the tag.
tag[1] is an array with the posts for the tag, so tag[1].size is the post count.

  1. Basically, we would need to capture something like tag[1].size#tag[0], which would result in a string like this:

    3#TagWithThreePosts 1#TagWithOnePost 2#TagWithTwoPosts
    
  2. Then, in the {% assign sortedtags = ... line, we're splitting that again and sorting it, so the result is a sorted array of strings:

    • 1#TagWithOnePost
    • 2#TagWithTwoPosts
    • 3#TagWithThreePosts
  3. In the final loop, we loop this in reverse (=descending) order, split by # to get the tag name and the post count, and display the link.

The only problems are tags with 10 and more posts.
Since we're sorting strings, the result of step 2 would look like this:

  • 1#TagWithOnePost
  • 10#TagWithTenPostsWRONG ORDER, because it's a string!
  • 2#TagWithTwoPosts
  • 3#TagWithThreePosts

To fix this, I'm adding 1000 to the post count for the purpose of sorting. So 1#... and 10#... become 1001#... and 1010#..., and they are ordered properly.

I still want to display the actual post number (without 1000 added), so I'm appending it as the third item in the {% capture tags %} part:

{{ tag[1].size | plus: 1000 }}#{{ tag[0] }}#{{ tag[1].size }}

By the way, I'm linking to a tag page (/tags/#blah, e.g. all posts for all tags on a single page) which I implemented in a similar fashion as well, described here.

like image 107
Christian Specht Avatar answered Sep 22 '22 13:09

Christian Specht