Find and replace values in dict

Hello everyone,
I have a problem that I just can’t solve.
I want to find and replace a value in a dict.

I need a task that searches for the elements under Part1 in the dict Starting_situation and replaces them with the value from To_be_replaced.
For example, the Part1-A is replaced by AAA1234.
At the end the same DIct with the replaced values should remain.

I would be very grateful for a solution, because somehow all my jinja attempts do not lead to the desired goal.

Here is the data:

Starting_situation:
  - Level: x00
    Part1:
      - Part1-A
    Part2: true
    Part3:
      - A: Info
        B: 20%
        C: false
  - Level: y00
    Part1:
      - Part1-B
	  - Part1-C
    Part2: true
    Part3:
      - A: Info
        B: 43%
        C: true
To_be_replaced:
  Part1-A: AAA1234
  Part1-B: BBB4321
  Part1-C: CBA9876

The desired result should then be stored in a new variable Wished_result and look like this:

Wished_result:
  - Level: x00
    Part1:
      - AAA1234
    Part2: true
    Part3:
      - A: Info
        B: 20%
        C: false
  - Level: y00
    Part1:
      - BBB4321
	  - CBA9876
    Part2: true
    Part3:
      - A: Info
        B: 43%
        C: true

The ansible.utils.replace_keys filter does exactly what you want, but with keys instead of values. It should be possible to use ansible_collections/ansible/utils/plugins/plugin_utils/replace_keys.py as a starting point and create a filter that does a similar thing with values.

Another approach would be to convert Starting_situation to a string with to_yaml or to_json, then loop over your To_be_replaced keys substituting the values with regex_replace, and finally converting the whole thing back with from_yaml or from_json as appropriate. This may be a bit shaky if you have “interesting” strings in To_be_replaced – i.e. regex meta-characters.

Here’s a working implementation of the latter method:

---
# brainmue01.yml
- name: Swapping values via map
  hosts: localhost
  gather_facts: false
  vars:
    Starting_situation:
      - Level: x00
        Part1:
          - Part1-A
        Part2: true
        Part3:
          - A: Info
            B: 20%
            C: false
      - Level: y00
        Part1:
          - Part1-B
          - Part1-C
        Part2: true
        Part3:
          - A: Info
            B: 43%
            C: true

    To_be_replaced:
      Part1-A: AAA1234
      Part1-B: BBB4321
      Part1-C: CBA9876
  tasks:
    - name: Overwrite Starting_situation variable with a fact
      ansible.builtin.set_fact:
        Starting_situation: '{{ Starting_situation | to_json | regex_replace("\b" ~ item ~ "\b", To_be_replaced[item]) }}'
      loop: '{{ To_be_replaced.keys() }}'  

    - name: Show the resulting fact
      ansible.builtin.debug:
        msg: '{{ Starting_situation }}'
3 Likes

You may want to consider using the “update_facts” module. See: ansible.utils.update_fact module – Update currently set facts — Ansible Documentation

4 Likes

@utoddl : Thank you very much, that is a very cool idea :bulb: to serialize the dict and then search and replace and then deserialize again.
The solution works perfectly in my case.

@binbashroot: Thanks for the tip, I’ll play with it too.

update_facts
I think it also has great potential and offers many possible applications.

Another option might be to use the combine filter?

2 Likes

Thank you, also a very interesting idea.
If I understand it correctly, then I would have to build the same structure with Part1 and then it would simply replace that. But searching for the right value makes it more difficult. But I think I can still use this for other challenges.

2 Likes

I tried using the combine filter on this problem, but everything I came up with (which wasn’t much) needed to have the parameters reversed. That is, instead of “A | combine(B)” I needed “B | combine(A)”. I’ve fruitlessly tried that before, so abandoned the attempt.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.