Data manipulation in Ansible: string transformation
It’s horrible. Kids, never do this!
I have a list (let’s say ‘data’), which contains stings. I need to add prefix and suffix to each string without involving Ansible loops (for example, because it’s already within a loop or two).
data:
- string1
- string2
We want to convert it to (with pure power of jinja2):
new_data:
- prefix_string1_postfix
- prefix_string2_postfix
The solution
new_data: '{{ (["prefix_"] * data|length)|zip(data)|map("join")|zip(["_suffix"] * data|length)|map("join")|list }}'
Em… Xk’uzme?
The solution, explained
suffixes: '{{["_suffix"] * data|length }}'
prefixes: '{{["prefix_"] * data|length }}'
data_prefixes: '{{prefixes|zip(data)|map("join")|list}}'
new_data: '{{data_prefixes|zip(suffixes)|map("join")|list}}
The solution, explained, explained
suffixes: '{{["_suffix"] * data|length }}'
prefixes: '{{["prefix_"] * data|length }}'
suffixes
: array of ‘_suffix’ * N times, where N == length(data).prefixes
: array of ‘prefix_’ * N times, where N == length(data).
Both uses Python trick, that [‘a’]*3 == [‘a’, ‘a’, ‘a’]
. We need those arrays to have the same size as our 'data' array to use zip filter.
data_prefixes: '{{prefixes|zip(data)|map("join")|list}}'
prefixes|zip(data)
creates list of list: [[‘prefix_’, ‘string1’], [‘prefix_’, ‘string2']]
.
map(“join”) apply ‘join’ (without arguments) to inner list. It becomes: [‘prefix_string1’, ‘prefix_string2']
. Last ‘list
’ is just to help debug
with output (map returns python’s genexpr
instead of list, so we convert it back to list). We don’t need it in full production environment.
new_data: '{{data_prefixes|zip(suffixes)|map("join")|list}}
Exactly the same trick. map data_prefixes with suffixes to create a nested list:
[['prefix_string1', '_suffix'], ['prefix_string2', '_suffix']]
Then we join and converting to list again:
['prefix_string1_suffix', 'prefix_string2_suffix']
.
Postscriptum
I’d like to say ‘never use this in production’, except that I develop this horrible technique to avoid more extended horrors in my production. Sad, sad, jinja2.
Regexps?
The next day after I had wrote this article, akint showed me a simpler solution. I ashamed, because this time regexps make things simpler:
new_data: '{{ data | map("regex_replace", "^(.*)$", "prefix_\1_suffix") | list }}'