I have an include which renders my posts in a certain format. I am passing the posts to the include like below:
{% include post-list.html posts=site.posts %}
However, I would like to filter out a certain category before passing it to the include. Does anyone have any ideas how I can achieve this?
You can do it defining a variable containing an array with all the posts not containing a specific category and then passing that to includes, for example to filter posts containing the category jekyll:
We create the array {% assign notjekyllposts = "" | split: "" %}
Loop all posts {% for apost in site.posts %}
Filter those that does not contain the jekyll category adding them to the array: {% assign notjekyllposts = notjekyllposts | push: apost %}
Pass the variable to the include tag
Putting it all together:
{% assign notjekyllposts = "" | split: "" %}
{% for apost in site.posts %}
{% unless apost.categories contains 'jekyll' %}
{% assign notjekyllposts = notjekyllposts | push: apost %}
{% endunless %}
{% endfor %}
{% include post-list.html posts=notjekyllposts %}
Related
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 would like to organize a page based on the number of pages that pass a filter.
I have tried to append truthy pages to a collection but it doesn't work.
{% assign homepage_posts = [] %}
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | concat: [my_page] %}
{% endif %}
{% endfor %}
<h1>size{{homepage_posts.size}}</h1>
<h1>{{homepage_posts}}</h1>
This is not working. Does concat only work with strings?
Jekyll will use Liquid 4 soon. But, for now, no concat.
In your case you can :
Create an empty array (bracket notation doesn't work in liquid) : {% assign homepage_posts = "" | split:"/" %}
{{ homepage_posts | inspect }} --> output : []
And push elements in it :
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | push: mypage %}
{% endif %}
{% endfor %}
{{ homepage_posts | inspect }}
concat filter only works with arrays and will be available in Jekyll when it upgrades to Liquid 4.*:
concat
Concatenates (combines) an array with another array. The resulting
array contains all the elements of the original arrays. concat will
not remove duplicate entries from the concatenated array unless you
also use the uniq filter.
To filter pages containing a specific attribute (in this case homepage: true) you can use a where filter.
Having a page with front matter:
---
homepage: true
---
Then you can have the pages with the homepage: true attribute like:
{% assign homepages = site.pages | where:"homepage","true" %}
Assuming I have a list of tags: iFix_6.3, iFix_7.0, iFix_7.1, iFix_8.0, announcement, and so on... and I want to run an operation only on certain several tags. How can I check for these multiple values?
There is contains, but I'm looking for the opposite of it and for multiple values...
Here is an example where I actually filter out all posts that contain the iFix_6.3 tag, thus display all other posts. This actually does not work yet... plus needs to be extended to work for multiple tags.
// posts with iFix_xxx tag should be filtered from the main posts view.
{% assign postUpdates = site.posts | where_exp:"item", "item.tags != 'iFix_6.3'" %}
{% for post in postUpdates limit:10 %}
<div class="postItem inline">
<p class="postDate">{% if post.pinned %}<span class="glyphicon glyphicon-pushpin"></span>{% endif %}{{post.date | date: '%B %d, %Y'}}</p>
<p class="postTitle">{{post.title}}</p>
</div>
{% endfor %}
You can do this by building an array of excluded tags (excluded_tags) using split and a string of the comma-separated tags. Then for each post, you iterate on the post's tags. Check if the tag is in excluded_tags using contains, if it is then raise the flag filtered_out to not display the post, using the unless control flow tag.
{% assign excluded_tags = "iFix_6.2,iFix_6.3,announcement" | split : "," %}
{% for post in site.posts limit:10 %}
{% assign filtered_out = False %}
{% for tag in post.tags %}
{% if excluded_tags contains tag %}
{% assign filtered_out = True %}
{% break %}
{% endif %}
{% endfor %}
{% unless filtered_out %}
...
{% endunless %}
{% endfor %}
Seems unless would solve the case.
{% assign postUpdates = site.posts | where_exp:"item", "unless item.tags contains 'iFix_6.3'" %}
My Jekyll side uses the default feed.xml. I would like to change that because I have site.posts and site.screencasts. Both have the needed attributes to be shown in feed.xml. The default runs this loop:
{% for post in site.posts limit:10 %}
...
{% endfor %}
Is there a way to merge site.posts and site.screencasts and than sort them by date and than limit the result to 10?
I assume that your site.screencasts are derived from page or post and all have a date in front matter.
Starting with an empty array helper in _config.yml
emptyArray: []
Then :
{% assign pagesArray = site.emptyArray %}
{% for post in site.posts %}
{% assign pagesArray = pagesArray | push: post %}
{% endfor %}
{% for scr in site.screencasts %}
{% assign pagesArray = pagesArray | push: scr %}
{% endfor %}
{% assign sorted = pagesArray | sort: "date" %}
{% for s in sorted limit: 10 %}
<h1>{{ s.title }}</h1>
{% endfor %}
I want to loop over all posts that are assigned category "foo" and category "bar" ..
{% for post in site.categories.foo and in site.categories.bar %}
Is this possible?
In my case "foo" as a "parent" category to "bar" ... /foo/bar/_posts
Thanks
Instead of looking through every post and matching with an or, you can filter by the first tag and then look for the second (and third, fourth, fifth...) tags:
{% for post in site.categories.fizz%}
{% if post.categories contains "buzz" and post.categories contains "bang" %}
<!--post has categories fizz AND buzz AND bang-->
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
This is a little more efficient than iterating over every single post, and it sets up an and relationship instead of an or relationship.
It is fully possible: loop over all posts, and then select the wanted posts:
{% for post in site.posts %}
{% if post.categories contains "foo" or post.categories contains "bar" %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
Using the Where Expression filter in Jekyll.
Jekyll Liquid Filter Docs
Where Expression,
Select all the objects in an array where the expression is true.
3.2.0
{{ site.members | where_exp:"item", "item.projects contains 'foo'" }}
So on my site I did:
_includes/clippings.html
...
{% capture _filter %}item.tags contains '{{ include.tag }}'{% endcapture %}
{% for clip in site.clippings | where_exp: 'item', _filter %}
{{ clip.do_stuff }}
# more html and stuff
{ endfor }
{% include clippings.html tag='foo' %}
In this case I need to specify the filter tag dynamically. And clippings is just a collection like posts.
If you want to filter by multiple static tags you could do something like:
{% for post in site.posts | where_exp: 'item', "item.tags contains 'foo'" | where_exp: 'item', "item.tags contains 'bar'" %}
{{ post.do_stuff }}
{ endfor }
If you want to do multiple dynamic filter tags then you will need to do something similar to the capture stuff I did above.
I have not tested this, but it should filter posts by an arbitrary amount of filter tags.
{% assign posts = site.posts %}
{% filter_tags = 'foo, bar, buzz' | slipt: ', ' %}
{% for tag in filter_tags %}
{% capture _filter %}item.tags contains '{{ tag }}'{% endcapture %}
{% assign posts = posts | where_exp: 'items', _filter %}
{% endfor %}
{% for post in posts %}
{{ post.do_stuff }}
{% endfor %}
However looping over the whole thing once and checking each post might be more efficient at that point.