У меня есть два словаря, как показано ниже
"fictional_characters": {
"male": [
"Donkey",
"Humpty",
"Piranha"
],
"female": [
"Fiona",
"Kitty_Softpaws",
"Diane_Foxington"
]
}
И
"movie_names": {
"Donkey": "Shrek",
"Humpty": "Puss_in_Boots",
"Piranha": "The_Bad_Guys",
"Fiona": "Shrek",
"Kitty_Softpaws": "Puss_in_Boots",
"Diane_Foxingtin": "The_Bad_Guys"
}
Я хотел бы изменить значения словаря в «fictional_characters» на значения в «movie_names», например.
"fictional_characters": {
"male": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
],
"female": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
]
}
Я начал с преобразования словаря "fictional_characters" в список
- name: Convert fictional_characters to a list
set_fact:
fictional_characters_list: "{{fictional_characters | dict2items }}"
Это дало мне
"fictional_characters_list": [
{
"key": "male",
"value": [
"Donkey",
"Humpty",
"Piranha"
],
},
"key": "female",
"value": [
"Fiona",
"Kitty_Softpaws",
"Diane_Foxington"
]
}
]
Далее немного Джинджи
- name: Using Jinja to swap dict values
set_fact:
fict_char_movies: |
{% for e in fictional_characters_list %}
{{ e.key }}:
{% for char in e.value %}
{% if char in movie_names %}
- {{ movie_names[char]|split(',') %}
{% endif %}
{% endfor %}
{% endfor %}
- name: Print the result
debug:
msg: "{{ fict_char_movies | from_yaml }}"
Приведенное выше возвращает следующий результат
"male": [
[
"Shrek"
],
[
"Puss_in_Boots"
],
[
"The_Bad_Guys"
],
"female": [
[
"Shrek"
],
[
"Puss_in_Boots"
],
[
"The_Bad_Guys"
]
]
Как мне избавиться от этого вложенного списка, чтобы получить структуру ниже?
"fictional_characters": {
"male": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
],
"female": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
]
}
Заявления ниже
fict_char_movies_str: |
{% for k,v in fictional_characters.items() %}
{{ k }}:
{% for char in v %}
{% if char in movie_names %}
- {{ movie_names[char] }}
{% endif %}
{% endfor %}
{% endfor %}
fict_char_movies: "{{ fict_char_movies_str|from_yaml }}"
The template can be simplified
fict_char_movies_str: |
{% for k,v in fictional_characters.items() %}
{{ k }}:
{{ v|map('extract', movie_names) }}
{% endfor %}
Дай то, что хочешь
fict_char_movies:
female:
- Shrek
- Puss_in_Boots
- The_Bad_Guys
male:
- Shrek
- Puss_in_Boots
- The_Bad_Guys
If you want to iterate set_fact the task below gives the same result
- set_fact:
fict_char_movies: "{{ fict_char_movies|d({})|combine(_dict|from_yaml) }}"
loop: "{{ fictional_characters|dict2items }}"
vars:
_dict: "{ {{ item.key }}: {{ item.value|map('extract', movie_names) }} }"
Example of a complete playbook for testing
- hosts: localhost
vars:
fictional_characters:
female:
- Fiona
- Kitty_Softpaws
- Diane_Foxington
male:
- Donkey
- Humpty
- Piranha
movie_names:
Diane_Foxington: The_Bad_Guys
Donkey: Shrek
Fiona: Shrek
Humpty: Puss_in_Boots
Kitty_Softpaws: Puss_in_Boots
Piranha: The_Bad_Guys
fict_char_movies_str: |
{% for k,v in fictional_characters.items() %}
{{ k }}:
{{ v|map('extract', movie_names) }}
{% endfor %}
fict_char_movies: "{{ fict_char_movies_str|from_yaml }}"
tasks:
- debug:
var: fict_char_movies
Шаблон можно упростить. Я обновил ответ. Пожалуйста.
Я добавил упрощенную итерацию set_fact.
Отличный и краткий ответ, и он стоит того, чтобы проголосовать!
Решение Владимира, вероятно, чище, но также можно добраться до того же места без использования шаблона Jinja. Например, мы можем написать:
- hosts: localhost
gather_facts: false
vars:
fictional_characters:
male:
- Donkey
- Humpty
- Piranha
female:
- Fiona
- Kitty_Softpaws
- Diane_Foxington
- Meilin_Lee
movie_names:
Donkey: Shrek
Humpty: Puss_in_Boots
Piranha: The_Bad_Guys
Fiona: Shrek
Kitty_Softpaws: Puss_in_Boots
Diane_Foxington: The_Bad_Guys
Meilin_Lee: Turning_Red
tasks:
- set_fact:
new_fictional_characters: >-
{{
new_fictional_characters|combine(
{
'male': item.0|ternary(new_fictional_characters.male + [movie_names[item.0]], new_fictional_characters.male),
'female': item.1|ternary(new_fictional_characters.female + [movie_names[item.1]], new_fictional_characters.female),
}
)
}}
loop: >-
{{ fictional_characters.male|zip_longest(fictional_characters.female) }}
vars:
new_fictional_characters:
male: []
female: []
- set_fact:
fictional_characters: "{{ new_fictional_characters }}"
- debug:
var: fictional_characters
Обратите внимание, что я добавил здесь дополнительный символ, чтобы продемонстрировать, что это работает, когда списки имеют разную длину. Запуск этого производит:
PLAY [localhost] ***************************************************************
TASK [set_fact] ****************************************************************
ok: [localhost] => (item=['Donkey', 'Fiona'])
ok: [localhost] => (item=['Humpty', 'Kitty_Softpaws'])
ok: [localhost] => (item=['Piranha', 'Diane_Foxington'])
ok: [localhost] => (item=[None, 'Meilin_Lee'])
TASK [set_fact] ****************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"fictional_characters": {
"female": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys",
"Turning_Red"
],
"male": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
]
}
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Еще более простое решение — добавить в filter_plugins/swap_names.py следующее:
def filter_swap_names(val, name_map):
return {k: [name_map[name] for name in v] for k, v in val.items()}
class FilterModule:
def filters(self):
return dict(swap_names=filter_swap_names)
А затем напишите плейбук следующим образом:
- hosts: localhost
gather_facts: false
vars:
fictional_characters:
male:
- Donkey
- Humpty
- Piranha
female:
- Fiona
- Kitty_Softpaws
- Diane_Foxington
- Meilin_Lee
movie_names:
Donkey: Shrek
Humpty: Puss_in_Boots
Piranha: The_Bad_Guys
Fiona: Shrek
Kitty_Softpaws: Puss_in_Boots
Diane_Foxington: The_Bad_Guys
Meilin_Lee: Turning_Red
tasks:
- set_fact:
fictional_characters: "{{ fictional_characters|swap_names(movie_names) }}"
- debug:
var: fictional_characters
Голосуйте за! Это тоже отличный ответ, особенно в части filer_plugins!
Спасибо @larsks, я не знаком с методом «filter_plugins/swap_names.py», но выглядит неплохо. Я обязательно буду использовать это в будущем
Документация по написанию собственных плагинов фильтров может оказаться полезной. Как правило, я думаю, что это гораздо лучший способ обработки сложных преобразований ваших данных.
Спасибо, @Vladimir, я также попробовал подход {% for k,v в вымышленных_символах.items() %}, но он дал мне те же результаты. В обоих вариантах я использовал " |split(',') ", поэтому я создавал проблему, которую мне нужно было решить самостоятельно. Еще раз спасибо !