Reputation: 63
Given the following list:
fruits:
- fruit: apple
color: red
texture: crunchy
shape: round
- fruit: grapefruit
color: yellow
taste: sour
- fruit: pear
color: yellow
How would I use the items2dict filter to change to a dictionary (below)? Problem is there is multiple, and a variable amount of values.
"{{ fruits | items2dict(key_name='fruit', value_name='??????') }}
Desired result:
fruits:
- apple:
color: red
texture: crunchy
shape: round
- grapefruit:
color: yellow
taste: sour
- pear:
color: yellow
I can't seem to find how here: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#transforming-lists-into-dictionaries
Upvotes: 5
Views: 7545
Reputation: 68074
The task below does the job
- set_fact:
fruits2: "{{ fruits2|default([]) + [{ item['fruit']: value }]}}"
loop: "{{ fruits }}"
vars:
keys: "{{ item.keys()|difference(['fruit']) }}"
vals: "{{ keys|map('extract', item)|list }}"
value: "{{ dict(keys|zip(vals)) }}"
gives
fruits2:
- apple:
color: red
shape: round
texture: crunchy
- grapefruit:
color: yellow
taste: sour
- pear:
color: yellow
A dictionary, instead of a list, can be created by simply changing the concatenation of the lists to the combination of the dictionaries e.g.
- set_fact:
fruits3: "{{ fruits3|default({})|combine({ item['fruit']: value }) }}"
loop: "{{ fruits }}"
vars:
keys: "{{ item.keys()|difference(['fruit']) }}"
vals: "{{ keys|map('extract', item)|list }}"
value: "{{ dict(keys|zip(vals)) }}"
gives
fruits3:
apple:
color: red
shape: round
texture: crunchy
grapefruit:
color: yellow
taste: sour
pear:
color: yellow
items2dict can be used to select 2 attributes only (key_name
and value_name
) e.g.
- set_fact:
fruits4: "{{ fruits|items2dict(key_name='fruit', value_name='color') }}"
gives
fruits4:
apple: red
grapefruit: yellow
pear: yellow
A list (desired result) can't be created by items2dict
. items2dict
returns a dictionary.
items2dict
will fail if the attribute is missing e.g.
- set_fact:
fruits5: "{{ fruits|items2dict(key_name='fruit', value_name='taste') }}"
fails
The error was: KeyError: 'taste'
Upvotes: 1
Reputation: 14141
The items2dict()
function is not appropriate for your case, but you may use this simple approach instead:
rows = """\
fruits:
- fruit: apple
color: red
texture: crunchy
shape: round
- fruit: grapefruit
color: yellow
taste: sour
- fruit: pear
color: yellow"""
rows_changed = "\n".join([2*" " + row if row.startswith(4*" ") else row
for row in rows.split("\n")])
print(rows_changed)
The explanation:
rows.split("\n")
creates a list of rows from the rows
multi-line string,2*" " + row if row.startswith(4*" ") else row
within []
(the list comprehension) then indents rows starting with 4-space indent by another 2-space indent,"\n".join()
convert that (modified) list of rows back to the multi-line string.The output:
fruits: - fruit: apple color: red texture: crunchy shape: round - fruit: grapefruit color: yellow taste: sour - fruit: pear color: yellow
Upvotes: 0
Reputation: 95968
I think you can do something like:
- name: bla
set_fact:
res: "{{ res | default({}) | combine({ item['fruit']: item }) }}"
loop: "{{ fruits }}"
Upvotes: 4