Extracting data from a string in Templates

Hi everyone. I just started with Home Assistant and realize I have much to learn.

What I’m trying to do is extract some text from a calendar description and store it in a variable. For example, if I have this:

states.calendar.personal.attributes.description = "I would like to extract #Joe and not Bob. I would also like to extract #Jane"

Can I somehow loop through that description to pull out all the words that start with a ‘#’ and then drop the ‘#’ so that the resulting variable looks like this:

users = 'Joe and Jane’

  1. The farthest I was able to get with that was just simply checking if the ‘#’ exists in the string, but it does not extract the word after it and it definitely does not extract all of the matches, just finds first:
    {% if ‘#’ in ‘I would like to extract #Joe and not Bob. I would also like to extract #Jane’ -%}
    True
    {%- else -%}
    False
    {%- endif %}

  2. A different way I tried to do it was by setting a specific list of words that should be searched for:

    {% set msg = “” %}
    {% set users = [’#Joe’,‘Bob’,’#Jane’] %}
    {% for users in users %}
    {% if ‘#’ in users -%}
    True - {{users}}
    {%- set msg = msg + " and " + users -%}
    {%- else -%}
    False - {{users}}
    {%- endif -%}
    {%- endfor -%}
    {{ msg }}

I expected {{msg}} to print out at the end “#Joe and #Jane” but it did not do that either.

Of course, I would prefer option 1 so that I do not have to set predefined search terms (besides just '#"), but I would appreciate help on either one of those.

Thanks!

Here is another hacky way of doing it… assuming the value doesn’t start with # character… You can add additional conditions if needed.

{% set value = "would like to extract #Joe and not Bob. I would also like to extract #Jane" %}
{%- for item in value.split('#') %}
{%- if loop.index  != 1 %}
{{ item.split(' ')[0] }}
{%- endif %}
{%- endfor %}

This prints:

Joe
Jane
1 Like

Here is mine :slightly_smiling_face::

{% set string = 'I would like to extract #Joe and not Bob. I would also like to extract #Jane' %}
{% for word in string.split() %}
  {%- if '#' in word -%}
    {%- if not loop.last -%}
      {{ word.replace('#', '') + ' and ' }}
    {%- else -%}
      {{ word.replace('#', '') }}
    {%- endif -%}
  {%- endif -%}
{% endfor %}

Prints Joe and Jane

1 Like

@skalavala and @VDRainer , both of these are awesome and give me plenty to play with for the next few days. :slight_smile: Thanks!

So far tried the last option in the script and formatted it a bit just to work better in case I have more than two names. Now, how do I get the results into a variable so that I can use it in a data_template message like so:

data_template:
message: “{{results} It’s time for your appointment!”

I tried just inserting the entire template into the script and got various errors:

script1:
  sequence:
    - service: media_player.volume_set
      entity_id: media_player.bedroom_music
      data:
        volume_level: 0.5
    - service: tts.google_say
      entity_id: media_player.bedroom_music
      data_template:
        message: >-
          {% set string = 'I would like to extract #Joe and not Bob. I would also like to extract #Jane and #Maria' %}
          {% for word in string.split() %}
            {%- if '#' in word -%}
              {% if not loop.last -%}
              {{ word.replace('#', '') + ', ' }}
            {%- else -%}
              {{ 'and ' + word.replace('#', '') + ' - '}} 
            {%- endif -%}
           {%- endif -%}
          {% endfor %}
         It's time for your appointment   

The message that I hope would be sent to tts is: “Joe, Jane and Maria - It’s time for your appointment”

I got both variations to work perfectly (almost*) , just decided to go with the second option since it’s a fraction of a second faster and deals a bit better with line breaks.

The only problem with the @VDRainer script is that loop.last formating only works if the ‘#’ is the last word of the string. For example, if the string is this:

'I would like to extract #Joe and not Bob. I would also like to extract #Jane end'

The results will be:

Joe and Jane and

I guess this is happening because the word #Jane is not in the last loop, but I’m not really sure what to do with that.

Hi @trusty, played a little with it, but with no luck.
Found the solution in this topic.
Easier than before i think. :slightly_smiling_face:

{% set string = 'I would like to extract #Joe and not Bob. I would also like to extract #Jane and #Max and so on' %}
{% for word in string.split() if '#' in word %}
  {%- if loop.last %} and {% elif loop.first -%}{% else %}, {% endif -%}
  {{ word.replace('#', '') }}
{%- endfor %}
1 Like

Sweet! It is better than before!


Update: After additional testing, I found that if there is only 1 value to return, it will print “and Value”.

So I replaced this:
{%- if loop.last %} and {% elif loop.first %}{% else %}, {% endif -%}

With this:
{%- if loop.first %}{% elif loop.last %} and {% else %}, {% endif -%}