Daniel Kruk
Daniel Kruk

Reputation: 15

Ansible iteration over nested dictionary

With Ansible 2.10, I am trying to iterate over a nested dictionary. The dictionary looks like that:

var1:
  d1:
    t1:
      - p1
      - p2
    t2:
      - p1
  d2:
    t1:
      - p1

I would like the function return something like that:

  1. d1, t1, p1
  2. d1, t1, p2
  3. d1, t2, p1
  4. ...

For any help, I will be grateful!

Upvotes: 0

Views: 109

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68254

The "Jinja-free" version below

shell> cat next_loop.yml
- set_fact:
    _batch: []
- set_fact:
    _batch: "{{ _batch + [[ei.key, item.0.key, item.1]] }}"
  with_subelements:
    - "{{ ei.value|dict2items }}"
    - value
- set_fact:
    out: "{{ out|default([]) + _batch }}"
    - include_tasks: next_loop.yml
      loop: "{{ var1|dict2items }}"
      loop_control:
        loop_var: ei
    - debug:
        msg: "{{ item|join(',') }}"
      loop: "{{ out }}"

gives the same result

  msg: d1,t1,p1
  msg: d1,t1,p2
  msg: d1,t2,p1
  msg: d2,t1,p1

Upvotes: 1

Zeitounator
Zeitounator

Reputation: 44809

There are other (and possibly more convenient) ways to do this depending on your exact requirement. Changing your data structure might as well be an option.

Meanwhile, here is a solution to store the combinations in a list of 3 element tuples that you can reuse elsewhere.

---
- name: Product for nested dictionnary
  hosts: localhost
  gather_facts: false

  vars:
    var1:
      d1:
        t1:
          - p1
          - p2
        t2:
          - p1
      d2:
        t1:
          - p1

  tasks:
    - name: Level 1 product (d* X t*)
      set_fact:
        level1: >-
          {{
            level1 | default([])
            +
            [item] | product(var1[item] | list)
          }}
      loop: "{{ var1 | list }}"


    - name: Level 2 product (d* X t* X p*)
      set_fact:
        level2: >-
          {{
            level2 | default([])
            +
            [item] | product(var1[item.0][item.1]) | map('flatten')
          }}
      loop: "{{ level1 }}"

    - name: Result
      debug:
        msg: "{{ item.0 }}, {{ item.1 }}, {{ item.2 }}"
      loop: "{{ level2 }}"

Which gives:

PLAY [Product for nested dictionnary] **************************************************************************************************************************************************************************************************

TASK [Level 1 product (d* X t*)] *******************************************************************************************************************************************************************************************************
ok: [localhost] => (item=d1)
ok: [localhost] => (item=d2)

TASK [Level 2 product (d* X t* X p*)] **************************************************************************************************************************************************************************************************
ok: [localhost] => (item=['d1', 't1'])
ok: [localhost] => (item=['d1', 't2'])
ok: [localhost] => (item=['d2', 't1'])

TASK [Result] **************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=['d1', 't1', 'p1']) => {
    "msg": "d1, t1, p1"
}
ok: [localhost] => (item=['d1', 't1', 'p2']) => {
    "msg": "d1, t1, p2"
}
ok: [localhost] => (item=['d1', 't2', 'p1']) => {
    "msg": "d1, t2, p1"
}
ok: [localhost] => (item=['d2', 't1', 'p1']) => {
    "msg": "d2, t1, p1"
}

PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Upvotes: 1

Related Questions