Search code examples
ansibleansible-2.xansible-inventoryansible-factsansible-template

Convert ansible dictionary list values to keys and keys to values


I have a scenario where I need to convert this dictionary dict1 into dict2 in an Ansible playbook as shown below

dict1 = {'a':[1,2,3],'b':[4,5,6]}

dict2 = {1: 'a', 2: 'a', 3: 'a', 4: 'b', 5: 'b', 6: 'b'}

I tried with_nested, with_items but wasn't able to make it work.


Solution

  • A bit ugly but does the job without having to use set_fact in a task which gives you freedom to declare the var anywhere (e.g. inventory, playbook, role defaults or vars, external file...).

    The solution uses json_query (which needs additional collection and pip module install on the controller, see documentation) because items2dict is not able to use nested elements as key/value references.

    map('flatten') in the solution is a workaround for a "bug" in jmespath (library used by json_query) which is unable to directly address tuples returned by subelements: it transforms each element into a list where indexes can correctly be used in json_query

    I did not spend time to see if indexes could be transformed to ints like asked in your requirement. As is, the final items2dict transforms them back to string representations. If this is not acceptable you will have to search further.

    Note: the below example will work in ansible >= 2.10. With an older version you will have to stick some intermediate list filters inside the dict2 expression.

    The playbook:

    - hosts: localhost
      gather_facts: false
    
      vars:
        dict1: {'a':[1,2,3],'b':[4,5,6]}
        transform_q: >-
          [*].{"key": [1], "value": [0].key}
        dict2: "{{ dict1 | dict2items | subelements('value') | map('flatten')
          | json_query(transform_q) | items2dict }}"
    
      tasks:
        - debug:
            var: dict2
    

    Gives:

    PLAY [localhost] *******************************************************************************************
    
    TASK [debug] ***********************************************************************************************
    ok: [localhost] => {
        "dict2": {
            "1": "a",
            "2": "a",
            "3": "a",
            "4": "b",
            "5": "b",
            "6": "b"
        }
    }
    
    PLAY RECAP *************************************************************************************************
    localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0