Search code examples
jinja2

Does Jinja support variable assignment as a result of a loop?


I've been using Jinja and DBT for a month now, and despite reading a lot about it, I didn't quite figure out how to create a list from another, using a simple for loop like I would in Python. Just a toy example:

{%- set not_wanted_columns = ['apple', 'banana'] -%} 
{%- set all_columns = ['kiwi', 'peach', 'apple', 'banana', 'apricot', 'pineapple'] -%}

What I want is a list as so:

{% set filtered_columns = ['kiwi', 'peach', 'apricot', 'pineapple'] %}

Naturally, I don't want to manually write this result because the full list might be dynamic or too long. I'm not even sure if Jinja does actually support this, although I do think this is a common problem.


Solution

  • As you have probably read from the documentation:

    Please note that assignments in loops will be cleared at the end of the iteration and cannot outlive the loop scope. Older versions of Jinja had a bug where in some circumstances it appeared that assignments would work. This is not supported.

    Source: https://jinja.palletsprojects.com/en/3.1.x/templates/#for

    And I guess when you are speaking about

    using a simple for loop like I would in Python

    What you mean here is using a list comprehension.

    So, as showed in the documentation, Jinja is using filter to achieve this:

    Example usage:

    {{ numbers|select("odd") }} 
    {{ numbers|select("divisibleby", 3) }} 
    

    Similar to a generator comprehension such as:

    (n for n in numbers if test_odd(n))
    (n for n in numbers if test_divisibleby(n, 3)) 
    

    Source: https://jinja.palletsprojects.com/en/3.1.x/templates/#jinja-filters.select

    There are actual four of those filter acting as generator comprehension:


    So, in your case, a reject filter would totally do the trick:

    {%- set filtered_columns = all_columns
         | reject('in', not_wanted_columns) 
         | list 
    -%}
    

    But, if you really want, you could also achieve it in a for:

    {%- for column in all_columns if column not in not_wanted_columns -%}
      {% do filtered_columns.append(column) %}
    {%- endfor -%}
    

    The do statement being a way to use list.append() without oddities being printed out: https://jinja.palletsprojects.com/en/3.1.x/templates/#expression-statement.