2

I have a list of dicts like this:

list_of_dicts:
  - name: Item1
  - name: Item2
    type: special
  - name: Item3
  - name: Item4
    type: small

As you can see, not all items have a type attribute. Context: If no type attribute is set, that means the item is of the default type.

I would like to select all items which are NOT of type special.

I've tried this: list_of_dicts | rejectattr('type', 'equalto', 'special')

Unfortunately, this fails with a AnsibleUndefinedVariable: 'dict object' has no attribute 'type' error because some of the items do not have a type.

What is a good way to solve this?

Zulakis
  • 4,191
  • 14
  • 44
  • 75

2 Answers2

4

Edit: I actually didn't pay enough attention to the latest edit of the existing answer from @ranjandas and the following comments. My first solution is almost similar. I guess you are more interested by the second one.


Here are 2 different ways to achieve your requirement.

  1. The first solution uses only filters available in ansible by default: rejectattr you already mentionned and its counterpart selectattr. The idea is to add two lists. The first one is made by selecting all dicts not having the type atttribute. The second one is made by selecting dict having the type attribute and rejecting those where it equals special.

  2. For the second solution, I used the json_query filter which requires pip install jmespath on the controller. As you can see below it is much more compact.

The choice is yours !

The demo playbook:

---
- name: Show not so special items
  hosts: localhost
  gather_facts: false

  vars:
    list_of_dicts:
      - name: Item1
      - name: Item2
        type: special
      - name: Item3
      - name: Item4
        type: small

  tasks:
    - name: Use select/rejectattr
      debug:
        msg: >-
          {{
            (list_of_dicts | selectattr('type', 'undefined') | list)
            +
            (list_of_dicts | selectattr('type', 'defined') | rejectattr('type', 'eq', 'special') | list)
          }}

    - name: Use json_query
      vars:
        query: "[?type != 'special']"
      debug:
        msg: "{{ list_of_dicts | json_query(query) | list }}"

Which gives:

PLAY [Show not so special items] **********************************************************************************************************************************************************************************

TASK [Use select/rejectattr] **************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "name": "Item1"
        },
        {
            "name": "Item3"
        },
        {
            "name": "Item4",
            "type": "small"
        }
    ]
}

TASK [Use json_query] *********************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "name": "Item1"
        },
        {
            "name": "Item3"
        },
        {
            "name": "Item4",
            "type": "small"
        }
    ]
}

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Zeitounator
  • 1,090
  • 4
  • 12
3

You should use rejectattr to reject items that has type undefined and use selectattr to select the items that are equalto special.

{{ list_of_dicts | rejectattr("type", "undefined") | selectattr("type", "equalto", "special") | list }}

edit:

How about combining two lists like given below? I don't know whether this is the best possible way, but it works.

{{ list_of_dicts | selectattr("type", "undefined") | list }} + {{ list_of_dicts | rejectattr("type", "undefined") | rejectattr("type", "equalto", "special") | list}}
Ranjandas
  • 73
  • 7
  • I wrote in my question: `I would like to select all items which are NOT of type special.`. Your suggestion does the exact opposite, it selects the items which ARE of type special. – Zulakis May 19 '20 at 08:25
  • @Zulakis Sorry for that, I missed the key point when posting. I have updated the answer, could you see if that fits the purpose? – Ranjandas May 19 '20 at 09:26
  • Yeah, I'm sure your second suggestion will work. Upvoted. If possible, I would prefer a more concise way of achieving this (instead of combining two filtered lists). So, if you have any other ideas... ;-) – Zulakis May 19 '20 at 09:29