Convert 2 lists into a flat/condensed dictionary

Viewed 50

I feel like I'm losing my god damn mind about this. It seems so easy but I just can't make it work.

The completely first input data is this:

IPs: [ 'ip1', 'ip2' ]
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'}] }

Is there absolutely no way how to make it in Jinja2 WITHOUT using loop/with_items?

2 Answers

In kotlin, the solution is like this:

var DICT = mapOf("ip1" to "reg1", "ip2" to "reg2", "ip3" to "reg2", "ip4" to "reg3") 
var a = DICT.toList()
var b = a.groupBy{ it.second }.map{ "${it.key} : ${it.value.map{ it.first } }" }
println(b)

// output: [reg1 : [ip1], reg2 : [ip2, ip3], reg3 : [ip4]]

But I think, you are using Python. You should use group by and after that map to get the result that you want.

Take DICT first and create a dictionary of reg* and ip*

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

gives

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

Then, take IPs and get the reg*

  regs: "{{ IPs|map('extract', DICT) }}"

gives

  regs: [reg1, reg2]

Use the list regs and select the matching keys from var1. For example,

  var2: "{{ var1|dict2items|selectattr('key','in', regs)|items2dict }}"

gives what you are looking for

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

Example of a complete playbook

- hosts: localhost

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

    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 }}"

  tasks:
    - debug:
        var: var2
Related