[SOLVED] How to append items to list using Template?

Hi! Since one of the latest updates templates can now render lists. However the list workflow seems to be pretty limited. Here’s the problem I faced:
I’m trying to create a sensor with a list of links, parsed via IMTP integration. What I’ve got:

{% set body = state_attr('sensor.avito_clothes_mail', 'body') %}
{% set links = [] %}
{% for i in range(10) %}
    {{ links.append(body | regex_findall_index('https://www.avito.ru.+?(?=")', index = i)) }}
{% endfor %}
{{ links }}

But instead of the wanted list I receive an error: SecurityError: access to attribute 'append' of 'list' object is unsafe.

So here’s my question — how do I create lists using templates, when I’m not allowed to append items to it?

4 Likes

You can append items to a list by adding two lists, like this:

{% set data = namespace(numbers=[]) %}
{% for i in range(10) %}
    {% set data.numbers = data.numbers + [i] %}
{% endfor %}
{{ data.numbers }}

For your case, this should work (I cant test it and I am not sure about the “regex_findall_index” stuff):

{% set body = state_attr('sensor.avito_clothes_mail', 'body') %}
{% set data = namespace(links=[]) %}
{% for i in range(10) %}
    {% set data.links = data.links + [(body | regex_findall_index('https://www.avito.ru.+?(?=")', index = i))] %}
{% endfor %}
{{ data.links }}
15 Likes

Thank you, that really worked! Seems not obvious at all however :crazy_face:

1 Like

Hi @tobi-bo is there a way to set a list item to another value? and remove a specfic item from that list?

{% set data = namespace(numbers=[]) %}
{% set data.numbers = data.numbers + [2] %}
{% set data.numbers = data.numbers + [3] %}
{% set data.numbers[0] =  [2] %}
{{ data.numbers[0] }}

the line where i try to set value [0] from the list, does not work, however displaying only value[0] as in the last line, is no problem.

On the second problem, removing one item from the list, i assume you have to create a new list entirely with the remaining list items…

Thanks in advance!

1 Like

Thank you!
I guess this is an effective way to limit issues around mutability so I kind of get why they force you to do it that way

I’ve been trying to manipulate a list using that approach, but cannot get it to work. It must be a scoping issue, but apart from the condition, I cannot see my code being any different from the example above.

{% set list = ['A', 'B', 'C', 'D', 'E'] %}
{% set prefixed_list = [] %}
{% for item in list %}
  {% if item == "C" %}
    {{ item }}
    {% set prefixed_list = prefixed_list + [item] %}
    {{ prefixed_list }}
  {% else %}
    {{ item }}
    {% set prefixed_list = prefixed_list + ['the ' + item] %}
    {{ prefixed_list }}
  {% endif %}
{% endfor %}
{% set list = prefixed_list %}
{{ list }}

This returns:

A
['the A']

B
['the B']

C
['C']

D
['the D']

E
['the E']

[]

Yeah, thats a scoping issue, therefore you need the namespace(prefixed_list=[])

{% set list = ['A', 'B', 'C', 'D', 'E'] %}
{% set data = namespace(prefixed_list=[]) %}
{% for item in list %}
  {% if item == "C" %}
    {{ item }}
    {% set data.prefixed_list = data.prefixed_list + [item] %}
    {{ data.prefixed_list }}
  {% else %}
    {{ item }}
    {% set data.prefixed_list = data.prefixed_list + ['the ' + item] %}
    {{ data.prefixed_list }}
  {% endif %}
{% endfor %}
{% set list = data.prefixed_list %}
{{ list }}
1 Like

Thanks a lot that solved it!

If you’re interested, the same result, prepending the word the to all letters except the letter C, can be achieved using regex_replace.

{{ ['A', 'B', 'C', 'D', 'E']
  | map('regex_replace', '([^C])', 'the \\1') | list  }}

I’m struggling to understand, perhaps because I am so new to templating…
What I’m trying to do is average a list of numbers together, but there are times when they are “unavailable”.

How do I create a list and add the resulting value only if it is a number and then ultimately average those? Sometimes it will be 3 values getting averaged, sometimes 2 and rarely 1.

Is this possible?

This is the base of what I have come up with thus far that works when all three are available, but as soon as one is unavailable it breaks for obvious reasons.

{% if is_number(states('sensor.first_oil')) %}
  {% set first = states('sensor.first_oil')|float %}
  {% elif set %}
  {% endif %}

{% if is_number(states('sensor.second_oil')) %}
  {% set second = states('sensor.second_oil')|float %}
  {% endif %}

{% if is_number(states('sensor.third_oil')) %}
  {% set third = states('sensor.third_oil')|float %}
  {% endif %}


{{average(first,second,third)}}

Thank you!

Have you tried the min/max helper ? It looks like it will ignore unknown entities in the calculation (except sum)

Use the select() filter to remove unavailable entities… there’s no need to use all those if statements:

{{ ['sensor.first_oil', 'sensor.second_oil', 'sensor.third_oil'] 
| select('has_value') | expand 
| map(attribute='state') | map('float') | average }}
1 Like

I can’t believe it as how simple this is, but I believe this worked. I don’t even need to make a template sensor :joy:
Thank you!

That’s so simple too, I wish I came here before spending like 6 hours trying to make this work LOL.
I love to learn so I want to fool around with this to see how it works as it’s so much simpler.
Thank you!!

1 Like