What is the most efficient way to extract a sub list from another list with ansible filters?

Viewed 77

I have a list of dicts similar to this one:

addons_xml_settings:
  - addon_name: addon1
    settings:
      - xpath: /settings/setting[@id='setting_name1']
        value: "false"
      - xpath: /settings/setting[@id='setting_name2']
        value: "false"
      - xpath: /settings/setting[@id='setting_name3']
        value: "true"
  - addon_name: addon2
    settings:
      - xpath: /settings/setting[@id='setting_name4']
        value: "false"
      - xpath: /settings/setting[@id='setting_name5']
        value: "false"
      - xpath: /settings/setting[@id='setting_name6']
        value: "true"
  - ...

I would like to filter the addons_xml_settings list based on addon_name value in order to be able to loop over the list of settings as follows:

- name: "Force settings values for addon2"
  community.general.xml:
    path: "/addons_settings/addon2/settings.xml"
    xpath: "{{ xml_setting.xpath }}"
    value: "{{ xml_setting.value | default(omit) }}"
  loop: "{{ filtered_settings }}" 
  loop_control:
    loop_var: xml_setting

In this task, filtered_settings is the resulting list after having filtered addons_xml_settings. For instance, if I filter on addon_name=addon2, I would like filtered_settings to have the following content:

filtered_settings:
  - xpath: /settings/setting[@id='setting_name4']
    value: "false"
  - xpath: /settings/setting[@id='setting_name5']
    value: "false"
  - xpath: /settings/setting[@id='setting_name6']
    value: "true"

I ended up with the following to achieve the expected result.

  loop: "{{ addons_xml_settings | selectattr('addon_name', 'equalto', 'addon2') | first | dict2items | map(attribute='value') | last }}"

This works, but is it the right way to achieve it or is there a better / more optimal / more readable way to do it?

1 Answers

In your current attempt, the overly complicated part I see is:

dict2items | map(attribute='value') | last

You are doing this just to get the settings property of the dictionary, so a simpler version would be:

loop: >-
  {{ 
     (
       addons_xml_settings 
       | selectattr('addon_name', 'equalto', 'addon2') 
       | first
     ).settings 
  }}

or

loop: >-
  {{ 
     (
       addons_xml_settings 
       | selectattr('addon_name', 'equalto', 'addon2') 
     )[0].settings 
  }}

What you could also do is to make a dictionary from your list, which will help target the addon_name easily:

loop: >-
  {{ 
     (
       addons_xml_settings 
       | items2dict(
           key_name='addon_name', 
           value_name='settings'
       )
     ).addon2 
  }}

Given the playbook:

- hosts: localhost
  gather_facts: no
  vars:
    addons_xml_settings:
      - addon_name: addon1
        settings:
          - xpath: /settings/setting[@id='setting_name1']
            value: "false"
          - xpath: /settings/setting[@id='setting_name2']
            value: "false"
          - xpath: /settings/setting[@id='setting_name3']
            value: "true"
      - addon_name: addon2
        settings:
          - xpath: /settings/setting[@id='setting_name4']
            value: "false"
          - xpath: /settings/setting[@id='setting_name5']
            value: "false"
          - xpath: /settings/setting[@id='setting_name6']
            value: "true"

  tasks:
  - name: First fashion
    debug:
      msg: >-
        {{ 
          (
            addons_xml_settings 
            | selectattr('addon_name', 'equalto', 'addon2') 
            | first
          ).settings 
        }}

  - name: Second fashion
    debug:
      msg: >-
        {{ 
          (
            addons_xml_settings 
            | selectattr('addon_name', 'equalto', 'addon2') 
          )[0].settings 
        }}

  - name: Third fashion
    debug:
      msg: >-
        {{ 
          (
            addons_xml_settings 
            | items2dict(
                key_name='addon_name', 
                value_name='settings'
            )
          ).addon2 
        }}

Those three tasks all yields:

ok: [localhost] => 
  msg:
  - value: 'false'
    xpath: /settings/setting[@id='setting_name4']
  - value: 'false'
    xpath: /settings/setting[@id='setting_name5']
  - value: 'true'
    xpath: /settings/setting[@id='setting_name6']
Related