Search code examples
ansiblejinja2

Ansible: Convert 2 lists into a flat/condensed dictionary


REPOST Unable to edit previous question:

I have made a mistake in the instructions... It should be like this, basically need the IPs into array values under "swapped" dict.

The completely first input data is this:

IPs: [ 'ip1', 'ip2', 'ip3' ]
DICT: { 'ip1': 'reg1', 'ip2': 'reg2', 'ip3': 'reg2', 'ip4': 'reg3', ... } 

I managed to make 2 lists out of them which I thought would be easier.

list1: {{ DICT | dict2items() | selectattr('key', 'in', IPs) | list | map(attribute='value') | list }}
list2: {{ DICT | dict2items() | selectattr('key', 'in', IPs) | list | map(attribute='key') | list }}
-----
list1: [ 'reg1', 'reg2', 'reg2' ]
list2: [ 'ip1', 'ip2', 'ip3']

I need it condensed into this format

{ 'reg1': 'ip1', 'reg2': [ 'ip2', 'ip3' ] }

or this is also acceptable

{ 'reg1': [ 'ip1' ], 'reg2': [ 'ip2', 'ip3' ] }

This "gives" me what I want, but the moment I make it a dict it just does not condense on its own.

{{ dict(list1 | zip(list2) | list }} 
gives
{ 'reg1': 'ip1', 'reg2': 'ip2' }

The closest I've been so far is this but I just don't know how to convert the values back from the key/value list of dict.

{{ dict(dict(list2 | zip(list1)) | dict2items() | groupby(attribute='value') | list ) }}
gets me this \|/
{ 'reg2': [{'key': 'ip2', 'value': 'reg2'}, {'key': 'ip3', 'value': 'reg2'}], 'reg1': [{'key': 'ip1', 'value': 'reg1'}] }

If we have this:

IPs: [ 'ip1', 'ip2' ]
DICT: { 'ip1': 'reg1', 'ip2': 'reg2', 'ip3': 'reg2', 'ip4': 'reg3', ... } 

Result should be:

{ 'reg1': [ 'ip1' ], 'reg2': [ 'ip2' ] }

In @Vladimir-Botka's solution the problem is that it's not dynamically creating the arrays. It's simply grabbing whatever is defined in the original DICT.

arr: "{{ DICT|dict2items|groupby('value') }}"
keys: "{{ arr|map('first')|list }}"
vals: "{{ arr|map('last')|map('map', attribute='key')|list }}"
var1: "{{ dict(keys|zip(vals)) }}"
regs: "{{ IPs|map('extract', DICT) }}"
var2: "{{ var1|dict2items|selectattr('key','in', regs)|items2dict }}"

Result is

{ 'reg1': [ 'ip1' ], 'reg2': [ 'ip2' , 'ip3' ] }

If there was another IP defined in reg1, the final variable would reflect that but it's supposed to have only ip1, that is defined in IPs.


Solution

  • Given
    IPs: [ip1, ip2, ip3]
    DICT: {ip1: reg1, ip2: reg2, ip3: reg2, ip4: reg3}
    

    Use json_query to get the lists of keys. For example,

    arr: "{{ DICT|dict2items|groupby('value') }}"
    keys: "{{ arr|map('first')|list }}"
    vals: "{{ arr|map('last')|map('json_query', '[].key')|list }}"
    var1: "{{ dict(keys|zip(vals)) }}"
    

    give

    var1:
      reg1: [ip1]
      reg2: [ip2, ip3]
      reg3: [ip4]
    

    Then, select hashes where at least one item of the list is present in IPs

    regs: "{{ IPs|map('extract', DICT)|unique }}"
    var2: "{{ var1|dict2items|selectattr('key','in', regs)|items2dict }}"
    

    give

    var2:
      reg1: [ip1]
      reg2: [ip2, ip3]
    

    Example of a complete playbook

    - hosts: localhost
    
      vars:
    
        IPs: [ip1, ip2, ip3]
        DICT: {ip1: reg1, ip2: reg2, ip3: reg2, ip4: reg3}
    
        arr: "{{ DICT|dict2items|groupby('value') }}"
        keys: "{{ arr|map('first')|list }}"
        vals: "{{ arr|map('last')|map('json_query', '[].key')|list }}"
        var1: "{{ dict(keys|zip(vals)) }}"
        regs: "{{ IPs|map('extract', DICT)|unique }}"
        var2: "{{ var1|dict2items|selectattr('key','in', regs)|items2dict }}"
    
      tasks:
        - debug:
            var: var1
        - debug:
            var: var2