Ansible updating nested list

Viewed 358

I am working on an ansible task with will be updating the nested list in list. The input:

- name: test array of objects
  set_fact:
    list: 
       - name: x1
         surname: y1
         childrens: 
            - children1
            - children2
       - name: x2
         surname: y2
         childrens: 
            - children3
            - children4

For example, I want to add new children to / remove existing children from the object with params

name: x1
surname: y1

Example output (added children):

- name: test array of objects
  set_fact:
    list: 
       - name: x1
         surname: y1
         childrens: 
            - children1
            - children2
            - children3
       - name: x2
         surname: y2
         childrens: 
            - children3
            - children4

The task will process 1000+ records and should do it efficiently.

Thanks!

1 Answers

Add an index. For example fullname

    - set_fact:
        list: "{{ _list|from_yaml }}"
      vars:
        _list: |
          {% for i in range(3) %}
          - name: x{{ i }}
            surname: y{{ i }}
            fullname: x{{ i }}_y{{ i }}
            index: {{ i }}
            childrens:
              - children1
              - children2
          {% endfor %}

gives

  list:
    - childrens: [children1, children2]
      fullname: x0_y0
      index: 0
      name: x0
      surname: y0
    - childrens: [children1, children2]
      fullname: x1_y1
      index: 1
      name: x1
      surname: y1
    - childrens: [children1, children2]
      fullname: x2_y2
      index: 2
      name: x2
      surname: y2

Then convert the list to a dictionary, e.g.

    - set_fact:
        dict: "{{ list|items2dict(key_name='fullname', value_name='childrens') }}"

gives

  dict:
    x0_y0: [children1, children2]
    x1_y1: [children1, children2]
    x2_y2: [children1, children2]

Update the items of the dictionary, e.g.

    - set_fact:
        dict: "{{ dict|combine({item.fullname: _children}) }}"
      loop:
        - {fullname: x1_y1, add: [children3]}
      vars:
        _children: "{{ dict[item.fullname] + item.add }}"

gives

  dict:
    x0_y0: [children1, children2]
    x1_y1: [children1, children2, children3]
    x2_y2: [children1, children2]

Restore the list from the dictionary

    - set_fact:
        list2: "{{ dict|dict2items(key_name='fullname', value_name='childrens') }}"

gives

  list2:
    - childrens: [children1, children2]
      fullname: x0_y0
    - childrens: [children1, children2, children3]
      fullname: x1_y1
    - childrens: [children1, children2]
      fullname: x2_y2

This way, it takes only a couple of seconds to create 1000 items and update a few of them. For example


    - set_fact:
        list: "{{ _list|from_yaml }}"
      vars:
        _list: |
          {% for i in range(1000) %}
      ...

    - set_fact:
        dict: "{{ dict|combine({item.fullname: _children}) }}"
      loop:
        - {fullname: x101_y101, add: [children3]}
        - {fullname: x301_y301, add: [children3]}
        - {fullname: x501_y501, add: [children3]}
      ...

    - debug:
        msg: |
          {{ list2.100 }}
          {{ list2.101 }}
          {{ list2.102 }}
          {{ list2|length }}

gives

  msg: |-
    {'fullname': 'x100_y100', 'childrens': ['children1', 'children2']}
    {'fullname': 'x101_y101', 'childrens': ['children1', 'children2', 'children3']}
    {'fullname': 'x102_y102', 'childrens': ['children1', 'children2']}
    1000

Use lists_mergeby to merge all items, e.g.

    - set_fact:
        list3: "{{ list|
                   community.general.lists_mergeby(list2, 'fullname')|
                   sort(attribute='index') }}"
Related