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

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?

Instead of
selectattr(‘name’, ‘eq’, db_servers_names)
try this:
selectattr(‘name’, ‘in’, db_servers_names)

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

Thanks, your first answer is exactly what I was looking for. :slight_smile: