How to increment the counter inside a Liquid for loop? - jekyll

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"

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?)

Store result of a query in a variable ( jinja)

I am trying to put the result of a query in a variable but it doesn't work.
I am not sure what to do so it returns 0 as expected. Any ideas? I am using dbt and jinja.
With the below code the results_list variable is (Decimal('0'),))
MACRO
{% macro source_freshness(model, column_name) %}
{% set freshness_query %}
SELECT COUNT 0 AS count
{% endset %}
{% set results = run_query(freshness_query) %}
{% if execute %}
{% set results_list = results.columns[0].values() %}
{% else %}
{% set results_list = [] %}
{% endif %}
{{ return(results_list) }}
{% endmacro %}
call in a model:
{% set freshness_query_test = source_freshness(ref('model'),'date') %}
{% if count in freshness_query_test == 0 %}
do this
{% else %}
do that
{% endif %}
Thank you!
thanks for your help with this. I have not been able to find a direct answer but what I have done is to add the macro in a separate model, and then use the call statement logic in the shared answer Hi, how do we define select statement as a variable in dbt?

jekyll: combine limit and where and reverse

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.

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.

Jinja2 for loop behaving similarly to with

I'd like to iterate over a set of objects and find the maximum of one particular attribute, however jinja2 ignores any action within an iterator on a variable declared outside of the iterator. For example:
{% set maximum = 1 %}
{% for datum in data %}
{% if datum.frequency > 1 %}
{% set maximum = datum.frequency %}
{% endif %}
{% endfor %}
{# maximum == 1 #}
datum.frequency is definitely greater than 1 for some datum in data.
EDIT (solution)
This is similar to this post, but there's a bit more to it. The following works and is very ugly.
{% set maximum = [1] %}
{% for datum in data %}
{% if datum.freq > maximum[-1] %}
{% if maximum.append( datum.freq ) %}{% endif %}
{% endif %}
{% endfor %}
{% set maximum = maximum[-1] %}
Have you considered writing a custom filter to return the highest value of a particular attribute within your collection? I prefer to minimize the amount of logic I use in Jinja2 templates as part of maintaining a 'separation of concerns'.
Here is a link to a very good example of how one can be written in python:
Custom jinja2 filter for iterator
Once you have your filter returning the value you need access it by using '|' like so:
{% set maximum = datum|filtername %}