{% break %} in for loop template

Hi everyone,

does there something like “{% break %}” exist in HA templates?
In some cases I would expect this to speed up my templates a lot.

A (very simple example) would be a template like the following.
(it simply searches for a sensor named like iphone - mainly as example situation)

{% set data = namespace(h_bool=False) %}
{% for entity in states.sensor -%}
  {% if entity.entity_id | regex_search('.*iphone.*', ignorecase=True) %}{% set data.h_bool = True %}{% endif %}
{% endfor %}
{{ data.h_bool }}

As you see this really loops through ALL sensors while I would be done as soon as ONE iphone sensor is reached.

Never the less when I try to achive the same with something like:

{% for entity in states.sensor -%}
  {% if entity.entity_id | regex_search('.*iphone.*', ignorecase=True) %}true{% break %}{% endif %}
{% endfor %}

I receive the error:

TemplateSyntaxError: Encountered unknown tag ‘break’. Jinja was looking for the following tags: ‘elif’ or ‘else’ or ‘endif’. The innermost block that needs to be closed is ‘if’.

So I thought this maybe is related to the IF block, but moving the {% break %} outside of the if block does not help either:

{% for entity in states.sensor if entity.entity_id | regex_search('.*iphone.*', ignorecase=True) -%}
  true{% break %}
{% endfor %}

TemplateSyntaxError: Encountered unknown tag ‘break’. Jinja was looking for the following tags: ‘endfor’ or ‘else’. The innermost block that needs to be closed is ‘for’.

Which leads me to my initial question:
Does there something like "{% break %} " even exist in HA templates?

No, jinja2 does not have break.

Do you find your templates to be slow somehow?
Not sure how you notice that, but what is the reason you believe you need to speed it up?

Well not being slow when executing a single one in Developer Mode for e.g.

Never the less by using the “trace timeline” I could find some automations using these kind of loops to evaluate values at the beginning, are taking at least something like 3 seconds which in some cases is already very “long”.

And trying to break the for loop was one of my ideas for optimization :slight_smile:

No, but for the example you posted, you don’t even need a for-loop.

{{ states.sensor | selectattr('entity_id', 'search', 'iphone') | list | count > 0 }}

ah thanks for that hint…
yes a comparison with count > 0 is an option here, too.

I will check how I can transfer this to some of my loops.
(as said this was just an easy example to explain my situation more better :wink: )

Generally speaking, if a for-loop is required and you wish to minimize the number of iterations, you specify a constraint within the first line of the for-loop’s definition. Your second example was almost correct; here’s what I mean:

{% set ns = namespace(found = false) %}
{% for entity in states.sensor if entity.entity_id | regex_search('.*iphone.*', ignorecase=True) -%}
  {% set ns.found = true %}
{% endfor %}
{{ ns.found }}

However that’s still just a longer way to achieve what the example I posted does in one line.

1 Like

A slight modification of your code as shown below can emulate a loop ‘break’:

{% set data = namespace(h_bool=False) %}
{% for entity in states.sensor if data.h_bool == false %}
  {% if entity.entity_id | regex_search('.*iphone.*', ignorecase=True) %}
    {% set data.h_bool = true %}
  {% endif %}
{% endfor %}
{{ data.h_bool }}
1 Like

What is gained by this longer example that attempts to emulate a break when it has already been demonstrated it’s unnecessary?

The original goal was “I would be done as soon as ONE iphone sensor is reached”.
This code achieves that by “implementing” a break in Jinja which would exit the loop at first match.
It saves time if you have a long list with the first match at the head as
it doesn’t even look up the rest of the list items.
Your {for if} constraint doesn’t iterate, but it does check all items though…
It uses namespace as global variable to work around the rule
that, unlike Python, Jinja variables inside a loop are all local.
On the other hand, Python doesn’t have the {for if} construct,
maybe that’s one reason it has a break statement.

It’s more efficient. Easily proven by performing a comparative test using an identical, randomized dataset.

Basically, if given the most favorable dataset, it’s more efficient? That’s hardly an unbiased test.

Anyway, the most efficient way to do this was posted earlier and doesn’t require a for-loop.

For anyone finding this thread while looking for this functionality, Home Assistant has now enabled break and continue for loops via Jinja Extensions as mentioned in the Templating documentation.

This code prints out 6, 7, 8, 9, each on their own line.

{% for x in range(10) %}
  {% if x > 5 %}
    {{ x }}
  {% endif %}
{% endfor %}

This code prints out only 6.

{% for x in range(10) %}
  {% if x > 5 %}
    {{ x }}
    {% break %}
  {% endif %}
{% endfor %}