-
-
Notifications
You must be signed in to change notification settings - Fork 495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is it possible to sort a collection by front-matter values? #898
Comments
Solved it (for now at least)! Ended up using the collections API to sort the array of returned posts, but wasn't sure if there was a way to do it within a template.
Additionally in my search I came across #338 which touched on the eleventy data structure some, and also lead me to this area of the documentation: https://www.11ty.dev/docs/collections/#collection-item-data-structure. As someone porting his site over from Jekyll, it was very frustrating to try to figure out what data was available to grab from a returned collection object and how it was namespaced (ie, confusing that Is work being currently done to improve the documentation on global variables/data objects available in templates/collections? If so, I'd be happy to contribute where needed to help make their structure clearer. |
Sorting via JavaScript is the best way that I've found. One option is directly in the For collections that might need to be sorted by different properties in different contexts, I think you could sort the collection with JavaScript in a JavaScript template, which ends in |
Based on how the docs recommend to use
I like this approach as I can use it on any of my collections, and I have lots of collections, and I don't want have to do a I do admit I'm a bit nervous since docs go into using the collections API for custom sorting rather than suggesting a simple approach such as this. Do others think this approach is inadvisable? |
I like that approach, doing the sorting in Eleventy I think doing the ordering inside the collection creation, as advised in the docs and as I mentioned above, only makes sense when you know a particular collection will always be sorted a particular way. That's what I think. I'm curious how others are approaching this. |
@paulshryock Glad to hear at least one person doesn't think I'm on crack. What still makes me nervous is, if the solution really is this simple, why didn't someone suggest something similar months ago? I'm definitely not a rocket scientist - there must be at least some downside to my approach. |
It may just be that there are tons of issues, and no one got around to posting a reply about this. 🤷♂️ |
@paulshryock yeah, that's probably the most likely, and also that I didn't close my own issue after solving it 😂 @ckot, that's a sweet solve, love the idea of filters as a method of sorting... i'll close this now since it seems like there's a lot of good ideas but people can keep discussing if they want! cheers, yall! |
I wanted to sort a bunch of pages, each about a different person, by the name of each person. (This is just to add to what others wrote above in the hope that it might help others like myself who happen up this issue.) I came up with this function: function sortByName(values) {
return values.slice().sort((a, b) => a.data.sortName.localeCompare(b.data.sortName))
} (I'm no JavaScript expert, but I added it to the Eleventy configuration ( module.exports = (config) => {
config.addFilter('sortByName', sortByName)
} And I added a ---
title: Epictetus
tags: people
sortName: Epictetus
--- To test it, I did this: <ul>
{%- for person in collections.people | sortByName -%}
<li><a href="{{ person.url }}">{{ person.data.title }}</a></li>
{%- endfor -%}
</ul>
<ul>
{%- for person in collections.people | sortByName | reverse -%}
<li><a href="{{ person.url }}">{{ person.data.title }}</a></li>
{%- endfor -%}
</ul> I saw what I expected, so I guess it worked. |
Is it possible to introduce more powe to collection? For example, for school event calendar purpose, it is simple to use a collection to have all events in the same day. But for a yearly summary of activities, there are needed to have collection that have all activities of the year, then sorted by subject (physic, math, etc.) then by month, and then the class (year 1, year 2, etc.) Now to perform these, one need to write special sort and filters in js. But that kinds of power is gnerally needed. |
Hi @kentsin, I would create an Then I would use an Eleventy filter to show all events from that colection filtered by a specific year. // .eleventy.js
module.exports = function(eleventyConfig) {
// Create a collection
eleventyConfig.addCollection('events', function(collectionApi) {
return collectionApi
// Start with markdown templates inside `events`
.getFilteredByGlob('events/**/*.md')
// Sort content alphabetically by `subject`
.sort((a, b) => {
const subjectA = a.data.subject.toUpperCase()
const subjectB = b.data.subject.toUpperCase()
if (subjectA > subjectB) return 1
if (subjectA < subjectB) return -1
return 0
})
// Sort content ascending by `month` (assuming `month` is a Number)
.sort((a, b) => Number(a.data.month) - Number(b.data.month))
// Sort content alphabetically by `class` (assuming `class` is a string like `Year 1`)
.sort((a, b) => {
const classA = a.data.class.toUpperCase()
const classB = b.data.class.toUpperCase()
if (classA > classB) return 1
if (classA < classB) return -1
return 0
})
})
// Create a filter
eleventyConfig.addFilter('filterByYear', (value, year) => {
// Filter by `year`
return Number(value.data.year) === Number(year)
})
} // events/my-event.md
---
title: My Event
year: 2021
month: 2
subject: Math
class: Year 1
---
Hello world. // 2021-events.md
## 2021 events
{% for event in collections.events | filterByYear: 2021 %}
{% if forloop.first == true %}<ul>{% endif %}
<li><a href="{{ event.url }}">{{ event.data.title }}</a></li>
{% if forloop.last == true %}</ul>{% endif %}
{% endfor %} Eleventy Docs |
@ckot Would you mind showing a minimal example? I have some trouble getting this to work. I have "order: n" in my frontmatter, but when I use this loop I keep getting illegal tag errors:
|
@iwm-donath, did you add the quoted code block into your Eleventy config? Your config file would need to return a function which includes that code, so something like this: // .eleventy.js
module.exports = function (eleventyConfig) {
// Create the filter function.
function sortByOrder(values) {
let vals = [...values]
return vals.sort((a, b) => Math.sign(a.data.order - b.data.order))
}
// Add the filter.
eleventyConfig.addFilter('sortByOrder', sortByOrder)
} If you're using Liquid, try skipping the |
Well, that wasn't my problem, but I feel stupid nonetheless: My test code was inside a md-file. As soon as I put it inside a njk-file it worked 🤦♂️ markdownTemplateEngine: "njk" is also useful here ... |
If it’s useful for anyone, I worked with @spl’s solution (thank you!) to come up with the following to sort by front matter .eleventy.js:
Overview page (here working with venues):
(Open for feedback on how to make this simpler and better!) |
Does the nunjucks sort filter (standard to the language) actually work? I cannot get it to work using the syntax
I much prefer the versatility of doing this in templating rather than having to create many collections just to serve a single use case. I know I could build custom filters but things like this feel pretty core basic. |
Agreed, this seems like functionality that is so common that it should be already implemented. I used Kirby before Eleventy and these simple sorting and filtering methods were all readily available (no need to create and customize it yourself). I also have been having some trouble using the sort() method. Simply trying to sort by an int within the front matter of a collection is giving me such a headache. |
I got this working, but got some unexpected results if I didn't have an {% for p in collections.nav | sort(false, false, 'data.order') %}
<p><a href="{{ p.url | url }}" data-order="{{ p.data.order }}">{{ p.data.title }}</a></p>
{% endfor %} OUTPUT<p><a href="/pages/zero/" data-order="0">ZeRo</a></p>
<p><a href="/pages/one/" data-order="1">OnE</a></p>
<p><a href="/pages/two/" data-order="2">TwO</a></p>
<p><a href="/pages/three/" data-order="3">tHrEe</a></p>
<p><a href="/pages/four/" data-order="3.33333">fOuR</a></p>
<p><a href="/pages/five/" data-order="4">FiVe</a></p>
<p><a href="/pages/eleven/" data-order="11">ElEvEn</a></p> And if I change it to sort by {% for p in collections.nav | sort(false, false, 'data.title') %} OUTPUT<p><a href="/pages/eleven/" data-order="11">ElEvEn</a></p>
<p><a href="/pages/five/" data-order="4">FiVe</a></p>
<p><a href="/pages/four/" data-order="3.33333">fOuR</a></p>
<p><a href="/pages/one/" data-order="1">OnE</a></p>
<p><a href="/pages/three/" data-order="3">tHrEe</a></p>
<p><a href="/pages/two/" data-order="2">TwO</a></p>
<p><a href="/pages/zero/" data-order="0">ZeRo</a></p> UPDATE Also reminded me of mozilla/nunjucks#1302. I think earlier versions of Nunjucks didn't support sorting by nested props. I think this was added in v3.2.3, per https://github.com/mozilla/nunjucks/blob/master/CHANGELOG.md#323-feb-15-2021. |
This worked for me. |
Since this thread seems to be a helpful source of reference, I'll add the method I used for sorting a collection in a Liquid template. In my case, I was creating a portfolio sorted reverse-chronologically by year (the "workYear" is an integer in the front matter). {% assign workCollection = collections.work | sort: "data.workYear" | reverse %}
{%- for work in workCollection -%}
<a href="{{ work.url }}">
<!-- and the rest of my html etc -->
</a>
{%- endfor -%} |
Let's say I have a template located at
_items/my-awesome-post.md
with the following front matter:I've loaded this template into a collection using
getFilteredByGlob
as referenced in the documentation:I'm wondering if it's possible to sort these collection items by the
order
property in my front-matter via my liquid templates as below or whether there's another way to do it via the liquid template engine or the Eleventy API. I've already tried doing the following and it doesn't appear to return anything.I also tried using the sort filter to grab the data but that didn't return anything either
{% assign items_by_order = collections.items | sort: data.order %}
The text was updated successfully, but these errors were encountered: