How to extract a list of values of a field within a list of dictionaries when a key matches another list of values

48 views
Skip to first unread message

jean-christophe manciot

unread,
Feb 5, 2022, 9:56:36 AM2/5/22
to Ansible Project
Let's assume the following fictional list of dictionaries:
                domain_definition:
                        - name: server11
                          cluster: cluster1
                          db_servers:
                                - server12
                                - server21
                          port: '8080'
                        - name: server12
                          cluster: cluster1
                          db_servers:
                                - server22
                          port: '8090'
                        - name: server13
                          cluster: cluster1
                          port: '8091'
                        - name: server21
                          cluster: cluster2
                          db_servers:
                                - server12
                                - server22
                          port: '9080'
                        - name: server22
                          cluster: cluster2
                          port: '9090'
The goal is to list all ports of used  'db_servers', so the expected list is:
        - '8090'
        - '9080'
        - '9090'
        - '8090'
        - '9090'

The constraint is to use one (some operation is performed for each item) and only one loop (the list is huge in reality).

The following does not work because selectattr expects a value, not a list of values:
        - name: Extracting a list of values of a field within a list of dictionaries when a key matches another list of values
          hosts:
                - localhost
          strategy: debug
          tasks:
                - name: Extracting a list of values of a field within a list of dictionaries when a key matches another list of values
                  vars:
                        domain_definition:
                                - name: server11
                                  cluster: cluster1
                                  db_servers:
                                        - server12
                                        - server21
                                  port: '8080'
                                - name: server12
                                  cluster: cluster1
                                  db_servers:
                                        - server22
                                  port: '8090'
                                - name: server13
                                  cluster: cluster1
                                  port: '8091'
                                - name: server21
                                  cluster: cluster2
                                  db_servers:
                                        - server12
                                        - server22
                                  port: '9080'
                                - name: server22
                                  cluster: cluster2
                                  port: '9090'
                        db_servers_names: "{{ item.db_servers |
                                                default([]) |
                                                list }}"
                        db_servers_used_ports:  "{{ domain_definition |
                                                        selectattr('name', 'eq', db_servers_names) |
                                                        map(attribute= 'port') |
                                                        list }}"
                  debug:
                        msg:
                                - "db_servers_names:            {{ db_servers_names }}"
                                - "db_servers_used_ports:       {{ db_servers_used_ports }}"
                  loop: "{{ domain_definition }}"

Any suggestion?

Todd Lewis

unread,
Feb 6, 2022, 6:25:26 PM2/6/22
to Ansible Project
Instead of
    selectattr('name', 'eq', db_servers_names)
try this:
    selectattr('name', 'in', db_servers_names)

Todd Lewis

unread,
Feb 6, 2022, 8:32:38 PM2/6/22
to ansible...@googlegroups.com, jean-christophe manciot

This produces the list you want:

---
- name: Extracting a list of values of a field within a list of dictionaries when a key matches another list of values
  hosts:
    - localhost
  strategy: debug
  tasks:
    - name: Extract a list of values of a field within a list of dictionaries when a key matches another list of values
      set_fact:
        db_used_ports: |
            {% set ports=[] %}
            {% for dd0 in domain_definition %}
            {%   set db_servers_names=dd0.db_servers|default([]) %}
            {%   for dd1 in domain_definition %}
            {%     if dd1['name'] in db_servers_names %}
            {%       set _ = ports.append(dd1.port) %}
            {%     endif %}
            {%   endfor %}
            {% endfor %}{{ ports }}"
      vars:
        domain_definition:
          - name: server11
            cluster: cluster1
            db_servers:
              - server12
              - server21
            port: '8080'
          - name: server12
            cluster: cluster1
            db_servers:
              - server22
            port: '8090'
          - name: server13
            cluster: cluster1
            port: '8091'
          - name: server21
            cluster: cluster2
            db_servers:
              - server12
              - server22
            port: '9080'
          - name: server22
            cluster: cluster2
            port: '9090'

    - name: Show the extracted ports
      debug:
        msg: "{{ db_used_ports }}"

jean-christophe manciot

unread,
Feb 7, 2022, 3:52:47 AM2/7/22
to Ansible Project
Thanks, your first answer is exactly what I was looking for. :-)
Reply all
Reply to author
Forward
0 new messages