jekyll: combine limit and where and reverse - jekyll

Using Jekyll i'd like to:
iterate through all the pages
where the page.path is not the current path
where the page.categories contains "featured"
reverse them(most recent first)
limit 3
i'm having problems when getting all those filters together
{% assign posts = site.posts | reverse %}
{% for post in posts limit:3 %}
{% if post.categories contains 'featured' and post.path != page.path %}
{% include card.html post=post %}
{% endif %}
{% endfor %}
right now the limit is not working properly because the inner if will prevent a few items from being rendered.

Assign 0 to a counter variable before entering your loop. Don't set a limit on the loop, but instead set another condition on your counter being below your limit, and increment the counter using Liquid's plus filter every time you meet your criteria and output the card.
{% assign posts = site.posts | reverse %}
{% assign counter = 0 %}
{% for post in posts %}
{% if counter < 3 and post.categories contains 'featured' and post.path != page.path %}
{% include card.html post=post %}
{% assign counter = counter | plus: 1 %}
{% endif %}
{% endfor %}
A more complex example
I'm basing all of this on the looping I use myself. My condition is a little more complex: I check for any shared tag with the current page. I've included this as a further example below.
{% assign counter = 0 %}
{% for post in site.posts %}
{% if counter < 4 %}
{% if post.url != page.url %}
{% assign isValid = false %}
{% for page_tag in page.tags %}
{% for post_tag in post.tags %}
{% if post_tag == page_tag %}
{% assign isValid = true %}
{% endif %}
{% endfor %}
{% endfor %}
{% if isValid %}
{% include article_card.html %}
{% assign counter = counter | plus: 1 %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
Why where fails
Although Liquid has a where filter, it can only do exact comparisons, so you have to reinvent the wheel like this in order to achieve the where-like scenario for more complex conditions. This does make for a lot of looping through site.posts, but Jekyll being a preprocessor means the penalty of using a somewhat inefficient improvised where-type looping is only a compile-time concern, not a runtime one. Even so, to mitigate this cost as much as possible, I opt for having the counter condition be the first that Jekyll calculates.

Related

For loop in Shopify not working over number 9

I'm having an issue with a for loop in Shopify. I'm sure it used to work, but I can't get it to work over the number 9 now.
{% assign productTag1 = Availability14 %} (in this example, the product has only 1 tag, which is Availability14)
{% assign avail_stop = false %}
{% for j in (0..15) %}
{% assign check_avail = 'Availability' | append:j %}
{% if productTag1 contains check_avail %}
{% assign avail_stop = true %}
{% capture tag_name %}{{check_avail}}{% endcapture %}
{% break %}
{% endif %}
{% endfor %}
{% if avail_stop %}
{% assign availability = check_avail | remove:'Availability' | plus:0 %}
{% endif %}
At the moment, I'm returning 1, not 14. I imagine it's something to do with the fact 14 includes a 1, but I can't wrap my head around it.
Any help is appreciated.
You have a {% break %} statement in your if. Once the if becomes true it will exit the loop instantly.
If you want to skip the next code you must use {% continue %} not {% break %}.
On my mind this is an issue with conditional operator. As you said, 14 contains 1, so why not simply use strict conditional operator like this:
{% if productTag1 == check_avail %}
{% assign has_stop = true %}
{% break %}
{% endif %}
(or did I miss something?)

How to increment the counter inside a Liquid for loop?

I am struggling to figure out how to increment the index variable within a for loop in Liquid/Jekyll. Currently, I have something along the lines of
{% for i in (0..num_posts) %}
{% if i < some_value %}
do_thing
{% else %}
{% endif %}
{% assign i = i|plus:1 %}
{% if i<some_value %}
do_another_thing
{% else %}
{% endif %}
{% endfor %}
The problem is, instead of incrementing i, it leaves i as the same value.
Things I have tried:
Using {% assign i = i|plus:1 %}.
Using {% increment i %}.
Using
{% assign j = i|plus:1 %}
{% assign i = j %}
I can't use the offset command either since the code doesn't always check only 2 if statements in the loop.
Any ideas?
Here i is not the index.
To get the current index use {{ forloop.index }}.
{% if forloop.index < 5 %}
Do something
{% endif %}
To assign your own custom index inside a loop you may use something like:
{% assign i = 0 %}
{% for thing in things %}
{% assign i = i | plus:1 %}
{% endfor %}
Just use
{% increment my_counter %}
Creates a new number variable, and increases its value by one every time it is called. The initial value is 0. Also works with decrement. But just if you only have one simple counter, can't reset and always starts at "0"

Set limit of posts to display in jekyll

I need to limit the number of posts that show in a list from a collection of many posts. I am currently checking each post with a category of a specific name and then breaking the loop after the first 4 posts have been found.
{% assign count = 0 %}
{% for post in site.faqs %}
{% if post.faq_category contains "category-name" %}
{% assign count = count | plus:1}
{% if count == 4 %}
{% break %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
However, this is not returning any results. I'm not sure if I just have the count in the wrong place or what.
I figured out that I had to set the count below the li element:
{% assign count = 0 %}
{% for post in site.faqs %}
{% if post.faq_category contains "category-name" %}
{% assign count = count | plus:1 %}
<li>{{ post.title }}</li>
{% if count == 4 %}{% break %}
{% endif %}
{% endif %}
{% endfor %}
Using the counter you can filter posts and just display when the counter is less than 4, otherwise use break as you did:
{% assign count = 0 %}
{% for post in site.faqs %}
{% if post.faq_category contains "category-name" %}
{% assign count = count | plus:1}
{% if count < 4 %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
A cleaner version can be done creating the array of faqs items of the desired category and then limiting the for loop to 4:
{% assign faqs = site.faqs | where_exp:"item",
"item.faq_category contains 'category-name'" %}
<ul>
{% for post in faqs limit:4 %}
<li>{{post.title}}</li>
{% endfor %}
</ul>

Looping through all list of posts not in some category in liquid

I want to loop through posts on a site except ones with the category unlisted. I'm able to do this by nesting an if statement inside the for loop, but this breaks down when I want to also specify a limit – the loop will run for 5 times only regardless of whether the post passes the check.
{% for post in site.posts limit: 5 %}
{% unless post.categories contains 'unlisted' %}
<!-- display post -->
{% endunless %}
{% endfor %}
I need to pass an already filtered list to the for loop, but I'm unable to do this mainly because I can't find a way to combine the where filter with contains and negation:
{% for post in site.posts | WHERE CATEGORIES NOT CONTAINS 'UNLISTED' | limit: 5 %}
<!-- display post -->
{% endfor %}
You can use a counter :
<ul>
{% assign postCounter = 0 %}
{% assign maxPost = 5 %}
{% for post in site.posts %}
{% unless post.categories contains 'unlisted' %}
<li>{{ post.title }}</li>
{% assign postCounter = postCounter | plus: 1 %}
{% if postCounter >= maxPost %}
{% break %}
{% endif %}
{% endunless %}
{% endfor %}
</ul>

How can I loop on all items where a certain field is not empty?

I want to loop over all items in a collection that have a certain field set (not empty). I tried this:
{% assign papers_with_demos=site.data.papers | where:"demo", not blank %}
{% if papers_with_demos.size > 0 %}
<h2>demos</h2>
{% for paper in papers_with_demos %}]
...
{% endfor %}
{% endif %}
but it does not work; all papers are returned.
My goal is that the heading "demos" will be shown, only if there is one or more paper with a demo.
If papers are being returned, you must be using data files. If a value is empty for a particular key in a data file it will return false. So you could check the data key with a forloop like:
{% for paper in site.data.papers %}
{% if paper.demo %}
something
{% endif %}
{% endfor %}
You'll find more info in the Data Files section of the Jekyll documentation.
Updated in response to your reply:
{% for item in site.data.items %}
{% if item.random %}
{% assign random-val = item.random %}
{% if random-val %}
<p>true</p>
{% endif %}
{% endif %}
{% endfor %}
This will return true if inside items.yml, you have - random: true, nothing if empty.