Vijesh
Vijesh

Reputation: 1208

Ansible get unique list of dictionaries based on a key

I have two lists:

list1: [ {'a': 'name1', 'id': 'ABC'}, {'ax': 'name2', 'id': 'DEF'} ]
list2: [ {'a': 'nameX', 'id': 'XYZ'}, {'ab': 'nameY', 'id': 'DEF'} ]

I need to derive another list, list3 which should contain the elements of list1 that is not present in list2 based on the key - id's value.

I've tried with jinja2 but not getting the desired output.

Playbook:

- name: test
  hosts: localhost
  connection: local
  vars:
  - list1: [ {'a': 'name1', 'id': 'ABC'}, {'ax': 'name2', 'id': 'DEF'} ]
  - list2: [ {'a': 'nameX', 'id': 'XYZ'}, {'ab': 'nameY', 'id': 'DEF'} ]

  gather_facts: False
  tasks:
  - set_fact:
      list3: |-
        {%- set dL = [] -%}
        {%- for i in list1 -%}
          {%- for j in list2 -%}
            {%- if (i['id'] != j['id']) and (i not in dL) -%}
              {{- dL.append(i) -}}
            {%- endif -%}
          {%- endfor -%}
        {%- endfor -%}
        {{- dL -}}

  - debug: var=list3

Output:

"list3": [
    {
        "a": "name1",
        "id": "ABC"
    },
    {
        "ax": "name2",
        "id": "DEF"
    }
]

Expected Output:

"list3": [
    {
        "a": "name1",
        "id": "ABC"
    }
]

How can I achieve this result?

Upvotes: 2

Views: 718

Answers (2)

β.εηοιτ.βε
β.εηοιτ.βε

Reputation: 39129

You could use rejectattr in order to reject all the id contained in a list populated from list2. This later list can be created using map to extract only the id from list2.

All this together gives this simple task:

- set_fact:
    list3: "{{ list1 | rejectattr('id', 'in', list2 | map(attribute='id')) }}"

Given the playbook:

- hosts: localhost
  gather_facts: no

  tasks:
    - set_fact:
        list3: "{{ list1 | rejectattr('id', 'in', list2 | map(attribute='id')) }}"
      vars:
        list1: [ {'a': 'name1', 'id': 'ABC'}, {'ax': 'name2', 'id': 'DEF'} ]
        list2: [ {'a': 'nameX', 'id': 'XYZ'}, {'ab': 'nameY', 'id': 'DEF'} ]

    - debug: 
        var: list3

This yields:

TASK [set_fact] ********************************************************
ok: [localhost]

TASK [debug] ***********************************************************
ok: [localhost] => {
    "list3": [
        {
            "a": "name1",
            "id": "ABC"
        }
    ]
}

Upvotes: 2

Eli Harold
Eli Harold

Reputation: 2301

Just use list comprehensions like so:

list1 = [ {'a': 'name1', 'id': 'ABC'}, {'ax': 'name2', 'id': 'DEF'} ]
list2 = [ {'a': 'nameX', 'id': 'XYZ'}, {'ab': 'nameY', 'id': 'DEF'} ]
key = 'id'
l2vals = [d[key] for d in list2]
list3 = [d for d in list1 if d[key] not in l2vals]
print(list3)

Output:

[{'a': 'name1', 'id': 'ABC'}]

Upvotes: 1

Related Questions