Jinjia2: help with dynamically generated string

For some automation I need to create a string of this structure:

[ aaaa, aaaa, bbbb, cccc, cccc, cccc, dddd, dddd ]

where aaaa, bbbb, cccc, dddd - some strings (attributes of some objects) which are repeated several times.
Number of repetitions - depends on these objects (varies 1…3).

First I decided to create that [...] string without commas:

[ aaaa aaaa bbbb cccc cccc cccc dddd dddd ]

Here is a simplified code:

{% set COMMAND = "[" %}
{% for state in states.switch -%}
{%- set PARAM = "aaaa" -%}
{%- set COUNT = 2 -%}
{%- set PARAM_STRING = (PARAM + " ") * COUNT -%}
{%- set COMMAND = COMMAND + PARAM_STRING -%}
COMMAND = {{ COMMAND + "\n"}}
{%- endfor %}
{% set COMMAND = COMMAND + "]" %}
COMMAND = {{ COMMAND + "\n"}}

What is supposed to happen here:

  1. The COMMAND string is initialized by “[”.
  2. For each object (let it be every switch entity):
  • the “aaaa” string is repeated COUNT times (with a whitespace);
  • then this “aaaa aaaa ” string is added to the COMMAND.
  1. Finally, the assembled COMMAND string is concatenated with “]”.

What I get in a fact:

COMMAND = [aaaa aaaa 
COMMAND = [aaaa aaaa 
COMMAND = [aaaa aaaa 
COMMAND = [aaaa aaaa 
COMMAND = [aaaa aaaa 
COMMAND = [aaaa aaaa 


COMMAND = []

Looks like the COMMAND variable is overwritten after each loop.
How to avoid this?

Well, it supposed to be smth like this C/C++ code:

int a = 0;
for (int i=0; i < something; i++)
{
  a = a + some_array[i];
}

Solved!

{% set ns = namespace(COMMAND = "[") %}
{% for state in states.switch -%}
{%- set PARAM = "aaaa" -%}
{%- set COUNT = 2 -%}
{%- set PARAM_STRING = (PARAM + " ") * COUNT -%}
{%- set ns.COMMAND = ns.COMMAND + PARAM_STRING -%}
COMMAND = {{ ns.COMMAND + "\n"}}
{%- endfor %}
{% set ns.COMMAND = ns.COMMAND + "]" %}
COMMAND = {{ ns.COMMAND + "\n"}}
{% set COMMAND_2 = ns.COMMAND | replace(" ",",") | replace(",]","]") %}
COMMAND_2 = {{ COMMAND_2 + "\n"}}

Output:

COMMAND = [aaaa aaaa 
COMMAND = [aaaa aaaa aaaa aaaa 
COMMAND = [aaaa aaaa aaaa aaaa aaaa aaaa 
COMMAND = [aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa 
COMMAND = [aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa 
COMMAND = [aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa 


COMMAND = [aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa ]


COMMAND_2 = [aaaa,aaaa,aaaa,aaaa,aaaa,aaaa,aaaa,aaaa,aaaa,aaaa,aaaa,aaaa]

Out of curiosity, what is the application for this?

BTW, paste this into the Template Editor:

[{{ ('aaaa,' * 12)[:-1] }}]

I am trying to generate a command for the vacuum.
Example: [2,2,3,3,3,16,16,17]
Each number is a “room number”. Number of repetitions = number of passes.

Great, I am at a very beginning)))
Thank you!

To create a repeating sequence of identical integers

{{ ('3' * 2) | map('int', 0) | list}}

Taras, I tried this approach but failed:

          {% set ns = namespace(COMMAND = null) -%}
          {%- set ROOM_FLAGS = expand('group.vacuum_clean_rooms') -%}

          {%- for flag in ROOM_FLAGS -%}
            {%- if flag.entity_id | regex_match("input_boolean.vacuum_clean_room_", ignorecase=False) and
                   is_state(flag.entity_id,'on') -%}
              {%- set ROOM = flag.entity_id.split("vacuum_clean_room_")[1] -%}
              {%- set ROOM_NUMBER = states('input_number.vacuum_clean_room_number_' + ROOM)|int -%}
              {%- set CLEAN_COUNT = states('input_number.vacuum_clean_room_count_' + ROOM)|int -%}
              {%- set ROOM_NUMBER_STRING = (ROOM_NUMBER|string * CLEAN_COUNT) | map('int', 0) | list -%}
              {%- set ns.COMMAND = ns.COMMAND ~ ROOM_NUMBER_STRING -%}
            {%- endif -%}
          {%- endfor -%}
          {{ ns.COMMAND }}

Output:
image
It is string (should be a list), also it transforms “16, 16” to “1, 6, 1, 6”.
Probably I made a mistake somewhere…

P.S. There is one more related topic:

I believed that I can pass the list as an argument.

Sorry but setting up the required entities to test your template is more effort than I am willing to invest.

The examples I posted were meant to demonstrate that it’s possible to build a list by other means (than string concatenation with a for-loop).

It’s ok, no problem.
Here is a simplified code w/o entities:

          {% set ns = namespace(COMMAND = null) -%}
          {%- set ROOM_FLAGS = expand('group.vacuum_clean_rooms') -%}
          {%- for flag in ROOM_FLAGS -%}
              {%- set ROOM_NUMBER_STRING = ("2" * 3) | map('int', 0)  | list -%}
              {%- set ns.COMMAND = ns.COMMAND ~ ROOM_NUMBER_STRING -%}
          {%- endfor -%}
          {{ ns.COMMAND }}

Same output:
image

It’s the for-loop that goofs it up. Each iteration creates a list but then handles it like a string and concatenates it to the result of the previous iteration.

You have to handle each iteration’s result as a list and append it to the previous list. Initialize COMMAND to [] which is an empty list. Replace the ~ operator with + in the line that combines the current result to COMMAND.

I got this:

          {% set ns = namespace(COMMAND = []) -%}
          {%- set ROOM_FLAGS = expand('group.vacuum_clean_rooms') -%}
          {%- for flag in ROOM_FLAGS -%}
              {%- set ROOM_NUMBER_STRING = ("2" * 3) | map('int', 0)  | list -%}
              {%- set ns.COMMAND = ns.COMMAND + ROOM_NUMBER_STRING -%}
          {%- endfor -%}
          {{ ns.COMMAND }}

image

Another option is:

          {% set ns = namespace(COMMAND = "") -%}
          {%- set ROOM_FLAGS = expand('group.vacuum_clean_rooms') -%}
          {%- for flag in ROOM_FLAGS -%}
              {%- set ROOM_NUMBER_STRING = ("2" * 3) -%}
              {%- set ns.COMMAND = ns.COMMAND + ROOM_NUMBER_STRING -%}
          {%- endfor -%}
          {%- set LIST = ns.COMMAND | map('int', 0)  | list -%}
          {{ LIST }}

But both the cases give a wrong list if some number is of two digits:

Yes, now the final result is a single valid list.

The list contains three consecutive instances of 2 for each iteration of the for-loop.

I don’t know what your goal is but that result is correct as far as what the code is designed to do.

As for the issue with 21, a double-digit value wasn’t specified in your original requirement. In fact, it didn’t even use numeric values but an alphabetic string. I can help you fix the double-digit problem but I suspect you will reveal yet another requirement and the template will have to be re-engineered again. :thinking:

No no no)))
The final examples are:
[1]
[1, 2]
[16]
[1, 16, 16]
[1, 1, 16, 5, 5, 5]
So possible repeats are 1…3, possible numbers are 1,2,3,…,99.

But - probably it does not worth these efforts, since the properly generated list cannot be used as a script argument…

The original trick I used doesn’t work with a multi-digit value. However, this does work for either single or multi-digit values. It requires a trailing space so that it serves as a delimiter for split.

{{ ('23 ' * 3).split() | map('int', 0) | list }}

FWIW, I seem to recall I have successfully passed a list as a script variable. I’ll have to review my notes to confirm that. Anyway, whether it did or not might have no bearing on how it behaves with the params option.

BTW, every time I have replied in this thread, the forum software kept reminding me that it was already marked as Solved so was I sure I want to keep posting in it? :upside_down_face:

Taras, once again thank you very much for these lessons, I appreciate it a lot!