Andrew
Andrew

Reputation: 4428

How do I append to each item in a list of strings?

I have a list of strings containing IP addresses. I want to append a port number to each of them. In python I would do it something like this:

ip_list = [(ip + ":" + port) for ip in ip_list]

...but Jinja doesn't support list comprehensions. At the moment I'm kludging the problem by building a new list one item at a time:

{%- set ip_list = magic() %}
{%- set new_ip_list = [] %}
{%- for ip in ip_list %}
  {%- do new_ip_list.append(ip + ":" + port) %}
{%- endfor %}

This is ugly and irritating in the middle of a template, and it feels like there should really be a better way to get the job done. Preferably a one-liner.

While I know this can be done with custom filters, I'm supplying a template to software I did not write (saltstack), so they are (as far as I know) unavailable to me.

Upvotes: 15

Views: 21874

Answers (5)

simohe
simohe

Reputation: 671

regex_replace can do this. It is available in ansible and saltstack:

magic() | map('regex_replace', '$', ':'~port) | list
  • map: apply the regex_replace filter to each list element (like listElement | regex_replace('$', ':'~port))
  • replace: replace end of string with : and port (so append it)
  • list: convert generator to list

Using a regexp is overkill, but my other tries were even more so. Unfortunately regex_replace does not exist in normal jinja.

Upvotes: 11

CrEOF
CrEOF

Reputation: 131

The way I've been doing it is using zip_longest to create lists in the list of strings then joining them.

ip_string_list | zip_longest([], fillvalue='some_port') | map('join') | list

of course if you have the port in a variable you can do:

ip_string_list | zip_longest([], fillvalue=port_string) | map('join') | list

You can include a separator in the join operation too:

ip_string_list | zip_longest([], fillvalue=port_string) | map('join', ':') | list

To prepend throw in a reverse:

ip_string_list | zip_longest([], fillvalue=port_string) | map('reverse') | map('join', ':') | list

I use this pattern all the time for building LDAP distinguished names.

Edit: This is how I do it in Ansible, zip_longest isn't in stock Jinja2

Upvotes: 3

idriss Eliguene
idriss Eliguene

Reputation: 897

just iterate on the list item ans use jinja2 to print element like this:

   - set_fact:
        list_ip: "{% for ip in ip_list %}{{ip}}:{{port}}{% if not loop.last %},{% endif %}{% endfor %}"

And the lisp_ip vars will have your string ip:port separate with commas. And you can then split it to obtains a list

lisp_ip.split(',')

Upvotes: 0

Ralph Bolton
Ralph Bolton

Reputation: 854

This is by no means pretty, but this worked for me. I needed to build a list of usernames with a "!" in front of each one. This list has to be placed on a line with commas between the users.

My list of users (set in vars, defaults or whatever) looks like:

excluded_users: ["fred","jim","bob","arthur"]

In my template I've got this:

Match user *,!root{% if excluded_users|length > 0 %},!{{ excluded_users|join(",!") }}{% endif %}

...once rendered and saved, it looks like this:

Match user *,!root,!fred,!jim,!bob,!arthur

Like I said, not particularly pretty ;-)

Upvotes: 2

Dan Garthwaite
Dan Garthwaite

Reputation: 3526

Not having list comprehensions in ANY language is annoying!

Could you use the "|format" filter?

{% for ip in magic() -%}
curl_host_port:
  cmd.run:
    - name: {{ "curl 'http://%s:80/'"|format(ip) }}
{% endfor -%}

Upvotes: 1

Related Questions