I have a collection in Jekyll which I want to sort. Sorting by title is easy of course.
<ul>
{% for note in site.note | sort: "title" %}
<li>{{note.path | git_mod }}: {{ note. title }}</li>
{% endfor %}
</ul>
I want to sort by date. But since collections don't have a date, I have a custom Liquid filter which takes the path of the item, and gets its last modified time in Git. You can see that in the code above, where I pass the path to git_mod. I can verify that this works, because when I print out the list, I get the correct last modified times, and it is a full date. (In practice, I also pass it to date_as_string.)
But I can't sort by that value because Liquid doesn't know about it, since it is a value already in each item in the site.note collection. How can I sort by that value? I was thinking something like this, but it doesn't work:
<ul>
{% for note in site.note | sort: path | date_mod %}
<li>{{note.path | git_mod }}: {{ note. title }}</li>
{% endfor %}
</ul>
I've also tried variants like: {% for note in site.note | sort: (note.path | git_mod) %}
None of these throw an error, but none of them work either.
This is a case where you can use Jekyll hooks.
You can create a _plugins/git_mod.rb
Jekyll::Hooks.register :documents, :pre_render do |document, payload|
# as posts are also a collection only search Note collection
isNote = document.collection.label == 'note'
# compute anything here
git_mod = ...
# inject your value in dacument's data
document.data['git_mod'] = git_mod
end
You then will be able to sort by git_mod key
{% assign sortedNotes = site.note | sort: 'git_mod' %}
{% for note in sortedNotes %}
....
Note that you cannot sort in a for loop. You first need to sort in an assign, then loop.
Related
In my Jinja template, model.DataType value can be user defined or built in. My requirenement is if model.DataType start with the three letters ARR, then do a specific operation.
Example of values:
ARRstruct124
ARR_int123
ARR123123
CCHAR
UUINT
etc.
{% set evenDataType = model.eventDataType %}
{%if evenDataType | regex_match('^ARR', ignorecase=False) %}
// do the operation
{%else%}
// do the operation
{% endif %}
With this template, I am getting the error
{%if evenDataType | regex_match('^ARR', ignorecase=False) %}
jinja2.exceptions.TemplateAssertionError: no filter named 'regex_match'
There is indeed no regex_match filter in the Jinja builtin filters. You might have found some examples using it, but this is an additional filter provided by Ansible, so it won't work outside of Ansible.
This said, your requirement does not need a regex to be fulfilled, you can use the startswith() method of a Python string.
So, you template should be:
{% set evenDataType = model.eventDataType %}
{% if evenDataType.startswith('ARR') %}
`evenDataType` starts with 'ARR'
{% else %}
`evenDataType` does not starts with 'ARR'
{% endif %}
I am currently trying to build a website that will display team members using Jekyll and was wondering if there was an elegant way to make iterate and group people from specific groups.
So for reference point what I have is a md file collection that works containing the
name: Person Name
team: Team
image: pnglinkhere
And I was wondering if it is possible to iterate programmatically with something like this?
{% for team in site.people | [some how get list of unique team attributes] %}
<h5>{{ team }}</h5>
{% for people in site.people %}
{% if people.team = team % }
<div>Show Person</div>
{% endif %}
{% endfor %}
{% endfor %}
Any solution that doesn't involve folder reorganization would be beneficial and appreciated. I'm mainly looking for a programmatic way so it makes changes easier and so if a new is added I don't have to copy-paste for the new team.
Thank you
Your idea looks ok. Check out group_by and where filters described at https://jekyllrb.com/docs/liquid/filters/.
Whatever filter or condition you use, you may need to assign your people to a variable.
Group By
Group an array's items by a given property.
{{ site.members | group_by:"graduation_year" }}
[{"name"=>"2013", "items"=>[...]},
{"name"=>"2014", "items"=>[...]}]
{% assign people_grouped_by_xyz = site.people | group_by: your_attribute %}
{% for team in people_grouped_by_xyz %}
...
Where
Select all the objects in an array where the key has the given value.
{{ site.members | where:"graduation_year","2014" }}
{% assign people_grouped_by_xyz = site.people | where: "your_attribute","value" %}
{% for team in people_grouped_by_xyz %}
...
I am attempting to create a related posts section. I have used loops and conditionals to achieve this before, but I wanted a more efficient and cleaner method. I used include variables to achieve a similar result, but for whatever reason if I attempt to use a post's front matter, I get an empty result. Example:
---
categories:
- Featured
---
{% assign featured-posts = site.posts | where: "categories", page.categories %}
{% assign featured-posts = site.posts | where: "categories", page.categories %}
where filter looks for a string in a string or in an array.
Here page.categories is an array to look for in an array. This will return an empty array.
My shortest way to get related posts with at least one common category.
{% assign related-posts = "" | split: "" %}
{% for c in page.categories %}
{% assign related-posts = related-posts | concat: site.categories[c] | uniq %}
{% endfor %}
In my front matter for some pages (not all) I have:
---
top-navigation:
order: 2
---
Using liquid I want to filter all site pages which have a top-navigation object and sort by top-navigation.order.
I'm trying sort:'top-navigation.order' but that's throwing an exception of undefined method [] for nil:NilClass. I tried where:"top-navigation", true but it's not equating truthy values.
How can I filter for pages that have top-navigation and then sort?
Two steps:
Create an array with pages that contains the top-navigation key.
We create an empty array and then push the items that have the key.
{% assign navposts = ''|split:''%}
{% for post in site.posts %}
{% if post.top-navigation %}
{% assign navposts = navposts|push:post%}
{% endif %}
{% endfor %}
Sort the above array by top-navigation.order
{% assign navposts = navposts|sort: "top-navigation.order"%}
Printing results:
{% for post in navposts %}
<br>{{ post.title }} - {{post.top-navigation}}
{% endfor %}
For pages use site.pages.
In Jekyll 3.2.0+ (and Github Pages) you can use the where_exp filter like so:
{% assign posts_with_nav = site.posts | where_exp: "post", "post.top-navigation" %}
Here, for each item in site.posts, we bind it to the 'post' variable and then evaluate the expression 'post.top-navigation'. If it evaluates truthy, then it will be selected.
Then, putting it together with the sorting, you'd have this:
{%
assign sorted_posts_with_nav = site.posts
| where_exp: "post", "post.top-navigation"
| sort: "top-navigation.order"
%}
Liquid also has the where filter which, when you don't give it a target value, selects all elements with a truthy value for that property:
{% assign posts_with_nav = site.posts | where: "top-navigation" %}
Unfortunately, this variant does not work with Jekyll.
I'm using jekyll-bootstrap to maintain a blog on GitHub.
I'd like to have a sorted tags_list. The tag with the most posts comes first. Then I can have a display that shows the first tags with bigger font-size and last tags with smaller font-size. And I also want a splice function.
If in python/Jinja2, I'd like some code like this:
{% for tag in sorted_tags[:10] %}
<li style="font-size:{{ tag.count }}px;">{{ tag.name }}</li>
{% endfor %}
What's the equivalent implementation in ruby/jekyll?
This is how I'm sorting by the number of posts in a tag (descending), without any plugins (i.e. GitHub Pages compatible).
It also works when your tag names contain spaces; only , and : are forbidden characters (but you can easily change those).
{% capture counts_with_tags_string %}{% for tag in site.tags %}{{ tag[1] | size | prepend:"000000" | slice:-6,6 }}:{{ tag[0] }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
{% assign counts_with_tags = counts_with_tags_string | split:"," | sort | reverse %}
<ol>
{% for count_with_tag in counts_with_tags %}
{% assign tag = count_with_tag | split:":" | last %}
{% assign count = site.tags[tag] | size %}
<li>{{ tag }} ({{ count }})</li>
{% endfor %}
</ol>
It's super gross. What it does:
counts_with_tags_string is set to a string like 000005:first_tag,000010:second_tag,000002:third_tag. The zero-padded numbers are generated using the filter chain | prepend:"000000" | slice:-6,6.
This is split on commas and sorted lexicographically, which works because of the zero padding. The result is assigned to counts_with_tags.
Finally, we iterate over the elements and split each on : to find the original tag name. We could find the count in the same way, but because it's zero padded, it's easier to look it up using site.tags[tag] | size instead.
I thought that the tags array is sorted. Assuming so, you can do this:
{% for tag in site.tags %}
<li style="font-size: {{ tag[1].size }}px">{{ tag[0] }}</li>
{% endfor %}
That feels a little hacky, but it should work. Unfortunately, Liquid doesn't currently allow you to sort arrays within your templates. If you want to do any sorting on the array, you'd probably have to write a plugin to do so - it shouldn't be too complex. In fact, there's an existing plugin for sorting accessors that may do it: https://github.com/krazykylep/Jekyll-Sort
I only needed to do this in one place, on the page where my list of Tags was listed, so I wrote it as a Jekyll filter:
tag_index.html
<h2>Posts by Tag</h2>
<ul class="tags-list">
{{ site.tags | render_tags_list }}
</ul>
_plugins/filters.rb
module Jekyll
module Filters
def render_tags_list(tags)
sorted_tags = tags.keys.sort_by! { |tag| tag.downcase }
str = ''
sorted_tags.each { |tag|
str << '<li>' + tags[tag].size.to_s + ' - ' + tag + '</li>'
}
str
end
end
end
You could certainly just allow the filter to return sorted_tags above if you wanted to keep a better separation between "view" logic and programming logic, but my case was very simple. Trying to re-access a hash value by a specific key using Liquid templating wasn't a very concise process, or maybe I was just doing it wrong, but was much easier in Ruby.
I'm hosting my blog on github and wanted a solution to sorting a tag list that did not involve any jekyll plugins since Github doesn't allow for custom plugins (jekyll bootstrap attempts this as well). My post here doesn't really answer the question since I sort by tag name, and NOT by size. You can adapt this method to output the tag size as well in the string and then do some fancier spliting to get a different sort order (but it will be messy)
I was able to do this with the following code:
{% capture tagString %}{% for tag in site.tags %}{{ tag[0] }}{% unless forloop.last %}|{% endunless %}{% endfor %}{% endcapture %}
{% assign tags = tagString | split: '|' | sort: 'downcase' %}
<div id="cloud">
{% for tag in tags %}
{% assign number = site.tags[tag].size %}
{% assign slug = tag | downcase | replace: ' ', '_' %}
<span class="{% if number == 1 %}small{% elsif number <= 5 %}medium{% elsif number <= 10 %}large{% else %}huge{% endif %}">
{{ tag | downcase }}
</span>
{% endfor %}
</div>
It's kind of odd since I capture a string of tags (using | as separator) and then use that to create an array. After that point (in the loop) I can refer to the tag as tag and the list of sites that use that tag as site.tags[tag].
I use this on my blog:
https://github.com/kelsin/kelsin.github.io/blob/master/tags/index.html
The rest of the code is just how I've chosen to make a tag cloud on my tag page. Hope this helps someone else looking for a solution (without plugins)!