Loop For()

Hi everybody,

I try to have a loop For() in a condition. I saw few examples with defined objects:
{% for state in states.light -%}

But I need something like that:
{%- set text = ... -%}
{%- set i = 0 -%}
{%- for i in range[0,10] -%}
{%- if text[i] ==... -%}

When I check the config, it’s OK but my automation doesn’t work !

I tried few syntax but nothing to do…

Do you have a solution?

Thank you !

This should be for i in range(0, 10). And I don’t think you need to set i before the loop.

1 Like

Thank you for this solution !

I’ve got another question about the For() loops…

How can I stop the loop ? I tried
{% break %}
but it doesn’t work !

Do you know how to do ?
Thank you !

You cannot break for loops. You are extremely limited with for loops in jinja. What is your overall goal? There may be a better way to handle this. With Jinja, you need to be very crafty with its limitations.

I have a If in a For() loop in a condition, and I need to stop immediately the For() when If is True !

To be clearer, I write an example with a stupid if:

{% for i in range(0, 10) %}
  {% if i+2>6 %}
    true
    {% break %}
  {% endif %}
{% endfor%}

Indeed, Jinja is really limited. I didn’t realized before…

Yeah, thats not possible with jinja. You need to use filters to remove items from your for loop. I avoid for loops if I can because of this limitation. If the value_template requires a single item, using a for loop will not give you the results you expect. In your example above, you could do something like this:

{%  set items = range(0,10) | reject("<", 4) | list %}
{{ items | length > 0 }}

That will return true if the list has items after rejecting everything that is less than 4. It will return false if the list rejected everything.

Like I said, if you know what you want to do in the end, I can help you with the code to get something that works.

EDIT: Just saw the i+2 > 6, changed the syntax to be equivalent by making it < 4 which is equivalent to i+2

My aim is to understand a sentence sent from Telegram.
To do this, I defined 2 (or more) lists of words.
After I’m watching word by word to see if the sentence contains a word of each list.
I tried with a very simple sentence:

- alias: 'good night'
  initial_state: 'on'
  trigger:
    - platform: event
      event_type: telegram_text
  condition:
    - condition: template
      value_template: >-
        {%- set text = trigger.event.data.text|lower -%}
        {%- set text = text.split(' ') -%}
        {%- set liste_1 = ["good","well"] -%}
        {%- set liste_2 = ["night","nights", "dream", "dreams"] -%}
        {% for i in range(0, 10) %}
          {% if (text[i] == liste_1[0] or text[i] == liste_1[1] or text[i] == liste_2[0] or text[i] == liste_2[1] or text[i] == liste_2[2] or text[i] == liste_2[3]) %}
            {% for j in range(i+1, 11) %}
              {% if (text[j] == liste_1[0] or text[j] == liste_1[1] or text[j] == liste_2[0] or text[j] == liste_2[1] or text[j] == liste_2[2] or text[j] == liste_2[3]) %}
                true
              {% endif %}
            {% endfor %}
          {% endif %}
        {% endfor %}
  action:
    - service: telegram_bot.send_message
      data_template:
        message: 'Good night!'
        title: ''
        target: '{{ trigger.event.data.chat_id }}'

With this solution, we can write:

  • good night,
  • have a good night
  • good night to you…

Well it becomes really easy, and every sentence with words in lists are well understand !!

My problem is with this part:
{% if (text[j] == liste_1[0] or text[j] == liste_1[1] or text[j] == liste_2[0] or text[j] == liste_2[1] or text[j] == liste_2[2] or text[j] == liste_2[3]) %}

I would like to have a loop here to don’t be obliged to write by hand, and just have to fill in the lists !
So I need to stop the loop immediately if a second word is find !

I hope to be clear…

thank you for your help !!

{% set text = trigger.event.data.text | lower %}
{% set text = text | replace('good', '*') %}
{% set text = text | replace('well', '*') %}
{% set text = text | replace('nights', '*') %}
{% set text = text | replace('night', '*') %}
{% set text = text | replace('dreams', '*') %}
{% set text = text | replace('dream', '*') %}
{{ text | reject("!=", "*") | list | length == 2 }}

This should work. It basically replaces everything in the sentence with your word, if you get 2 of them it passes, any more it fails. You can adjust to your liking.

If you use this method, make sure you replace plural words before non-plural, otherwise it won’t work.

2 Likes

Hi,

I’m deploying your solution, and it really cool !!

I have a question, how could I replace exact word ? I explain:

it doesn’t the difference between:

turn on
and 
return on

Indeed with replace ('turn','*'), it returns re* on

How Could I replace entier word ?

Thank you !

You’d need to use regex to get around that. That is why you should search for plurals first. You’re welcome to learn regex, but from what I’ve seen there is little to no information about the regex formatting used in templating. I haven’t looked hard though.

The regex filter you’d be looking for is:

regex_replace

documented here:

Hello, I know your question is old but now it is possible since Jinja version 2.10 (they added support for namespace objects).

{% set ns = namespace(found=false) %}
{% for item in items %}
    {% if item.check_something() %}
        {% set ns.found = true %}
    {% endif %}
    * {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}

My example is the following:

#below 21°C Tonight
{% set foo = namespace(found=False) %}
{% for i in states.weather.dark_sky.attributes.forecast -%}
  {%- if as_timestamp(now()) | timestamp_custom("%Y-%m-%d") in i['datetime'] %}
    {%- if i['temperature'] < 21 -%}
      {% set foo.found = True %}
    {%- endif %}
  {%- endif %}
{%- endfor %}
{{ foo.found }}

Just tested with Hassio 0.94.3 and it works for me!

10 Likes