Skip to content

Instantly share code, notes, and snippets.

@tannerdolby
Last active Jun 15, 2022
Embed
What would you like to do?
11ty filter for returning a sorted list of tags from collections. Use the it in a template like {{ collections.foo | taglist }} to get the sorted tag list.
eleventyConfig.addFilter("taglist", function(collection) {
const tags = [];
collection.forEach(post => {
tags.push(...post.data.tags);
});
const sorted = [...new Set(tags)].sort((a, b) => a.localeCompare(b));
return sorted;
});
@tannerdolby
Copy link
Author

tannerdolby commented Aug 5, 2021

This is useful when you have many posts with the same tag. It prunes away any duplicates and sorts the tags in alphabetical order. Can be iterated over in a template like:

some-post.njk

---
title: Some Post
---

{% set sortedTags = collections.posts | taglist % }

<ul>
  {% for tag in sortedTags %}
    <li>{{ tag }}</li> 
  {% endfor %}
</ul>

Or in a Markdown file:

some-post.md

---
title: Some Post
templateEngineOverride: njk, md
---

{% set sortedTags = collections.posts | taglist %}

<ul>
  {% for tag in sortedTags %}
    <li>{{ tag }}</li> 
  {% endfor %}
</ul>

@tannerdolby
Copy link
Author

tannerdolby commented Aug 14, 2021

More discussion based on this tweet:

Great tip but javascript syntax is so a mess to read for me. { ( => ... arrow function spread operator etc.
Would you add some comments to explain or use no shortcuts to help noobs ?

Here is an implementation with comments and less ES6 usage:

eleventyConfig.addFilter("taglist", function(collection) {
        // Creates an empty array to store tags in later on
        let tags = [];

        // iterate over the array of objects represented by the 
        // `collection` variable passed to the callback function
        for (var i = 0; i < collection.length; i++) {
            // collection[i].data.tags is an array in the form of
            // ["tag1", "tag2"] which is an iterable data structure.
            // We can use the spread operator (ES6 feature) `...` to "expand" 
            // the values present in each iterable array of tags then add them into
            // the `tags` array for later processing
            // tags.push(...collection[i].data.tags);

            // if we didn't want to use any ES6 and forego using spread syntax,
            // just loop through each iterable array of tags and push the tag values
            // into the `tags` array
            tags.push(collection[i].data.tags);
        }
        console.log(tags); // [['typescript', 'javascript'], ['scss', 'css']]

        // We pushed each tags array (which is iterable)
        // into the `tags` variable, so its now an array of arrays
        // since we want to "expand" each 1D tags array, we can simply
        // flatten the `tags` array so its only an array of tag strings
        flat = tags.flat(); 
        console.log(flat) // ['typescript', 'javascript', 'scss', 'css'] etc

        // The Set object allows us to store unique values of any type,
        // so we will convert our `tags` array to a Set and then
        // sort the unique list of tags in alphabetical order
        let set = new Set(flat.sort(function(a, b) {
            return a.localeCompare(b)
        }));

        // Returning a Set object with the alphabetically sorted taglist,
        // You could expand the set into an array if you want to return an array like I've
        // done in the first gist above, but either way works :)
        return set;
    });

as the comments introduce quite a bit of "bloat", the above code without comments looks like:

eleventyConfig.addFilter("taglist", function(collection) {
        let tags = [];
        for (var i = 0; i < collection.length; i++) {
            tags.push(collection[i].data.tags);
        }
        flat = tags.flat(); 
        let set = new Set(flat.sort(function(a, b) {
            return a.localeCompare(b)
        }));

        return set;
    });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment