Search code examples
pythonlistdictionaryansiblejinja2

How to form a dictionary of dictionaries with keys/values built from 3 different lists in Ansible


I'm pulling a data file off several remote hosts that has dynamically named columns and then rows of data. I'd like to compute a consolidated data structure. I cannot get json or csv from the source, just a text data file.

NODE1

field1  field2  field3
valueA1 valueA2 valueA3

NODE2

field1  field2  field3
valueB1 valueB2 valueB3

So far, I built 3 lists: The 1st is a list of node names. The 2nd & 3rd are formed from the data file on each host. The column names are the same in each data file, but I currently just build List2 with redundant names throughout as I read the remote data file.

List1

- node1
- node2
- node3

List2 of list

-
    - field1
    - field2
    - field3
-
    - field1
    - field2
    - field3
-
    - field1
    - field2
    - field3

List3 of list

-
    - valueA1
    - valueA2
    - valueA3
-
    - valueB1
    - valueB2
    - valueB3
-
    - valueC1
    - valueC2
    - valueC3

I'd like to end up with a dictionary of dictionaries like this:

{ node1: 
    {field1:valueA1, field2:valueA2, field3:valueA3},
  node2: 
    {field1:valueB1, field2:valueB2, field3:valueB3},
  node3:
    {field1:valueC1, field2:valueC2, field3:valueC3}
}

Suppose I could use a different data structure, but ultimately, I need the data nested like this into a variable for use later in the playbook and ultimately saved to a json file.


Solution

  • There are more options:

    • Use filter community.general.dict. For example, given the lists
    list1: [node1, node2, node3]
    list2: [field1, field2, field3]
    list3:
      - [valueA1, valueA2, valueA3]
      - [valueB1, valueB2, valueB3]
      - [valueC1, valueC2, valueC3]
    

    the declaration below

    result: "{{ dict(list1|
                     zip(list3|
                         map('zip', list2)|
                         map('map', 'reverse')|
                         map('community.general.dict'))) }}"
    

    does the job

    result:
      node1: {field1: valueA1, field2: valueA2, field3: valueA3}
      node2: {field1: valueB1, field2: valueB2, field3: valueB3}
      node3: {field1: valueC1, field2: valueC2, field3: valueC3}
    

    • Optionally, use Jinja. For example, given the lists
    list1: [node1, node2, node3]
    list2:
      - [field1, field2, field3]
      - [field1, field2, field3]
      - [field1, field2, field3]
    list3:
      - [valueA1, valueA2, valueA3]
      - [valueB1, valueB2, valueB3]
      - [valueC1, valueC2, valueC3]
    

    The declarations below give the same result

    result_str: |-
      {% for keys,values in list2|zip(list3) %}
      {{ list1[loop.index0] }}:
      {% for k,v in keys|zip(values) %}
        {{ k }}: {{ v }}
      {% endfor %}
      {% endfor %}
    result: "{{ result_str|from_yaml }}"
    

    • Given the simpler list2 with one set of the keys only
    list1: [node1, node2, node3]
    list2: [field1, field2, field3]
    list3:
      - [valueA1, valueA2, valueA3]
      - [valueB1, valueB2, valueB3]
      - [valueC1, valueC2, valueC3]
    

    The declarations below give the same result

    result_str: |-
      {% for values in list3 %}
      {{ list1[loop.index0] }}:
      {% for k,v in list2|zip(values) %}
        {{ k }}: {{ v }}
      {% endfor %}
      {% endfor %}
    result: "{{ result_str|from_yaml }}"