2

In my playbook, I have a list of dictionaries. I want to keep only duplicate dictionaries.

Basically it is something like:

{   
   "l": [
        {
            "a": "d",
            "b": "e",
            "c": "f",
        },
        {
            "a": "ggg",
            "b": "hhh",
            "c": "iii",
        },
        {
            "a": "jjj",
            "b": "kkk",
            "c": "lll",
        },
        {
            "a": "d",
            "b": "e",            
            "c": "f",
        }
        ]
}

I tried different things but at some point I realized I was perhaps reinventing the wheel. I couldn't find any module though.

EDIT
Here's what I've managed to do with jq, it's not very elegant because I'm reprocessing the list for each element (and I'm also making nulls to filter them afterwards). What reassures me is that gpt hasn't managed to do it at all (despite my insistence). The list is contained in the data object because the filter references that list explicitly.

---
- name: Filter duplicates from a list of dictionaries
  hosts: localhost
  gather_facts: no
  vars:
    jq_filter: '[ .l[] as $item | if ((.l | map(select(. == $item)) | length) > 1) then $item else null end | select (. != null) ] | unique'
    data:
      l:
        - { a: "ddd", b: "eee", c: "fff" }
        - { a: "ggg", b: "hhh", c: "iii" }
        - { a: "jjj", b: "kkk", c: "lll" }
        - { a: "ddd", b: "eee", c: "fff" }
        - { a: "jjj", b: "kkk", c: "lll" }
  tasks:
    - name: debug list
      debug:
        var: data
    - name: Process list
      shell: echo '{{ data | to_json }}' | jq '{{ jq_filter }}'
      register: shell_output
    - name: get duplicates
      set_fact:
        duplicates: "{{ shell_output.stdout_lines | join | from_json }}"
    - name: debug duplicates
      debug:
        var: duplicates 

I accepted the answer with a custom filter

2
  • Does Ansible: How to get duplicate items from list? answer your question?
    – U880D
    Commented Nov 14 at 18:24
  • 1
    @U880D I read that answer to, most are not applicable for object type. except the python filter. But thanks to your comment I read it one more time and noticed the use of json_query to extract counted elements. I think I can make a request with json_query filter (if it is like jq). perhaps it will solve my problem in one step.
    – Emmanuel
    Commented Nov 15 at 8:28

1 Answer 1

2

I think you're looking for

l | difference(l | unique) | unique

Unfortunately, the filter difference doesn't work with lists of dictionaries. But, you can write a filter on your own. For example,

shell> cat filter_plugins/duplicates.py 
def duplicates(l):
    return [i for i in l if l.count(i) > 1]


class FilterModule(object):

    def filters(self):
        return {
            'duplicates': duplicates,
        }

Then, the playbook below

- hosts: localhost

  vars:

    l:
      - {a: d, b: e, c: f}
      - {a: ggg, b: hhh, c: iii}
      - {a: jjj, b: kkk, c: lll}
      - {a: d, b: e, c: f}

    result: "{{ l | duplicates | unique }}"

  tasks:

    - debug:
        var: result | to_yaml

gives probably what you want

  result:
    - {a: d, b: e, c: f}

If you want to select the exact frequency, create the below filter

shell> cat filter_plugins/dict_counter.py 
def dict_counter(l):
    u = [dict(s) for s in set(frozenset(d.items()) for d in l)]
    return [{'dict': i, 'count': l.count(i)} for i in u]


class FilterModule(object):

    def filters(self):
        return {
            'dict_counter': dict_counter,
        }

gives

  l | dict_counter:
    - count: 2
      dict: {a: d, b: e, c: f}
    - count: 1
      dict: {a: ggg, b: hhh, c: iii}
    - count: 1
      dict: {a: jjj, b: kkk, c: lll}

Then, the below declaration gives the same result

  result: "{{ l | dict_counter
                | selectattr('count', 'eq', 2)
                | map(attribute='dict') }}"
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.