Jekyll: sort collections by size

Viewed 1106

I want to sort my Jekyll collections by the number of documents that are in each collection.

Each collection in the site.collections variable has a docs field, and the docs field (which is an array of documents) has a size field, which is the number of documents in this collection (see documentation).

However, something like this doesn't work:

{% assign sorted = site.collections | sort: 'docs.size' %}

{% for coll in sorted %}
  ...
{% endfor %}

It results in a

Liquid Exception: no implicit conversion of String into Integer

It seems that the argument to sort can only be an immediate field of the type of object being sorted, and not a field of a field thereof.

Is there a way to achieve sorting the collections by the number of documents they contain?

2 Answers
  1. Build an array of the available sizes:

    {% assign sorted = '' | split: "" %}
    {% for coll in site.collections %} 
        {% assign sorted = sorted| append: coll.docs.size %} 
    {% endfor %}
    
  2. Sort the above array.

  3. Iterate the above array and all your collections printing only the collection whose size matches the sorted array number.

Ok, I achieved it in a rather ugly way, along the lines of marcanuy's answer.

<!-- Create a comma-separated string of all the sizes of the collections -->
{% for coll in site.collections %}
    {% if coll.title %}
      {% if coll.docs.size < 10 %}
        {% assign str = coll.docs.size | prepend: "00" %}
      {% elsif coll.docs.size < 100 %}
        {% assign str = coll.docs.size | prepend: "0" %}
      {% else %}
        {% assign str = coll.docs.size %}
      {% endif %}
      {% assign sizes = sizes | append: str | append: "," %}
    {% endif %}
{% endfor %}

<!-- Remove last comma of string -->
{% assign length = sizes | size | minus: 1 %}
{% assign sizes = sizes | slice: 0, length %}

<!-- Split string into array, sort DESC, and remove duplicate elements -->
{% assign sizes = sizes | split: "," | sort | reverse | uniq %}

<!-- Iterate through sizes, and for each size print those collections that have this size -->
{% for s in sizes %}
  {% for coll in site.collections %}
    {% assign i = s | plus: 0 %}
    {% if coll.docs.size == i %}
      <p>{{ coll.title }}: {{ i }} documents</p>
    {% endif %}
  {% endfor %}
{% endfor %}

The main difficulty is that an array of sizes created like this, is an array of strings, and sorting it results in an alphabetical sort order, rather than in an numerical one (e.g. "15" comes before "2").

To remedy this, I prepend "00" to numbers less than 10, and "0" to number less than 100. This makes the alphabetical sort order coincide with the desired numerical sort order.

Then I iterate through these sizes (which are still strings), and convert them to integers on the fly (by plus: 0) so that I can compare them to the docs.size field of each collection.

It's pretty verbose, but since this is executed only when the site is generated, and not at each request in production mode, it's ok.

Still, better solutions are welcome!

Related