Get all categories belonging to one collection - jekyll

Using jekyll, I would like to get all categories that belong to exactly one of my collections. Currently, I have two collections defined in my _config.yaml:
collections:
posts:
output: true
permalink: /blog/:title/
press_releases:
output: true
permalink: /press/:title/
I learned that by using site.categories I can access all categories that are currently mentioned in my posts. The problem is that I would like to get only those categories related to either one of these two collections. Meaning only categories from press_releases OR categories from posts. Is there something like site.posts.categories or site.press_releases.categories?
At the moment, it seems that by calling site.categories I only get those categories from my collection posts.
I searched for other similar cases but haven't found a single one so far. Does jekyll not support separating categories according to collections or am I having a wrong idea of how to use collections and categories?

No, Jekyll does not provide an attribute to access the categories for just one collection. However, this can be done with some liquid code.
{% for my_doc in site.press_releases %}
{% for category in my_doc.categories %}
{% capture my_categories %}
{% if my_categories %}
{{ my_categories | join: "," }},{{ category }}
{% else %}
{{ category }}
{% endif %}
{% endcapture %}
{% endfor %}
{% endfor %}
{% assign my_categories = my_categories | split: "," | uniq %}
{% for cat in my_categories %}
{{cat }}
{% endfor %}
Basically what we do here is, get all documents of the desired collection, cycle through all documents and get the categories as stored in frontmatter (or defaults) and collect them in an array. Note, array operations in liquid are a bit clumsy, why we need to convert an array to a string back and forth. In the above code, the final for-loop should give you all categories of your collection 'press_releases'.

Related

Get list of Jekyll attribute to iterate through

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 %}
...

Can I use Front Matter in Jekyll Liquid Filters?

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 %}

Liquid filter collection where not null

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.

Filter Array Liquid in Jekyll

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 %}

Jekyll - Get all posts that are in multiple categories

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.