Ansible — заменить значения в словаре, перебирая другой словарь.

У меня есть два словаря, как показано ниже

"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"
   ]
}
Руководство для начинающих по веб-разработке на React.js
Руководство для начинающих по веб-разработке на React.js
Веб-разработка - это захватывающая и постоянно меняющаяся область, которая постоянно развивается благодаря новым технологиям и тенденциям. Одним из...
Разница между Angular и React
Разница между Angular и React
React и AngularJS - это два самых популярных фреймворка для веб-разработки. Оба фреймворка имеют свои уникальные особенности и преимущества, которые...
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Калькулятор CGPA 12 для семестра
Калькулятор CGPA 12 для семестра
Чтобы запустить этот код и рассчитать CGPA, необходимо сохранить код как HTML-файл, а затем открыть его в веб-браузере. Для этого выполните следующие...
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
О тренинге HTML JavaScript :HTML (язык гипертекстовой разметки) и CSS (каскадные таблицы стилей) - две основные технологии для создания веб-страниц....
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
1
0
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Заявления ниже

  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

Спасибо, @Vladimir, я также попробовал подход {% for k,v в вымышленных_символах.items() %}, но он дал мне те же результаты. В обоих вариантах я использовал " |split(',') ", поэтому я создавал проблему, которую мне нужно было решить самостоятельно. Еще раз спасибо !

piercjs 10.02.2023 15:55

Шаблон можно упростить. Я обновил ответ. Пожалуйста.

Vladimir Botka 10.02.2023 15:57

Я добавил упрощенную итерацию set_fact.

Vladimir Botka 10.02.2023 16:18

Отличный и краткий ответ, и он стоит того, чтобы проголосовать!

ayuuk ja'ay 10.02.2023 16:19

Решение Владимира, вероятно, чище, но также можно добраться до того же места без использования шаблона 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!

ayuuk ja'ay 10.02.2023 16:18

Спасибо @larsks, я не знаком с методом «filter_plugins/swap_names.py», но выглядит неплохо. Я обязательно буду использовать это в будущем

piercjs 10.02.2023 16:18

Документация по написанию собственных плагинов фильтров может оказаться полезной. Как правило, я думаю, что это гораздо лучший способ обработки сложных преобразований ваших данных.

larsks 10.02.2023 16:40

Другие вопросы по теме