listcountansible

Count number of occurrences of each unique element in a list in ansible


How can I count how many times is repeated each item in a list?

    ok: [] => {
        "list": [
            "5.8.3",
            "5.8.4",
            "5.8.4",
            "5.9.2",
            "5.9.2",
            "5.9.2"
        ]
    }
    

I want to print something like this:

    ok: [] => {
        "list_counter": [
            "5.8.3": 1
            "5.8.4": 2
            "5.9.2": 3
        ]
    }

I tryed something like this, but no success

    - set_fact:
        list_counter: '{{ item : 1 + item.get(unique_int[item],0) }}'
      loop: "{{ list }}" 

Solution

  • list_counter: "{{ list|community.general.counter }}"
    

    gives the expected result

    list_counter:
      5.8.3: 1
      5.8.4: 2
      5.9.2: 3
    

    shell> cat filter_plugins/my_counter.py 
    # -*- coding: utf-8 -*-
    # Copyright (c) 2021, Remy Keil <remy.keil@gmail.com>
    # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
    
    from __future__ import (absolute_import, division, print_function)
    __metaclass__ = type
    
    from ansible.errors import AnsibleFilterError
    from ansible.module_utils.common._collections_compat import Sequence
    from collections import Counter
    
    
    def my_counter(sequence):
        ''' Count elements in a sequence. Returns dict with count result. '''
        if not isinstance(sequence, Sequence):
            raise AnsibleFilterError('Argument for community.general.counter must be a sequence (string or list). %s is %s' %
                                     (sequence, type(sequence)))
    
        try:
            result = dict(Counter(sequence))
        except TypeError as e:
            raise AnsibleFilterError(
                "community.general.counter needs a sequence with hashable elements (int, float or str) - %s" % (e)
            )
        return result
    
    
    class FilterModule(object):
        ''' Ansible counter jinja2 filters '''
    
        def filters(self):
            filters = {
                'my_counter': my_counter,
            }
    
            return filters
    

    Use it

    list_counter: "{{ list|my_counter }}"
    

    list_counter: "{{ dict(data | unique |
                           zip(data |
                               json_query('[].{key:@, value: `1`}')|
                               groupby('key') |
                               map(attribute=1) |
                               map('map', attribute='value') |
                               map('sum') | list)) }}"