twigcraftcms

How to count duplicate values in an array


{% set valuesCars = ['bmw', 'mercedes', 'seat', 'porsche', 'seat', 'bmw', 'seat'] %}

{% set valuesIntegers = [27, 32, 36, 36, 27, 32, 32] %}

{% set counts = {} %}

{# Change valuesCars to valuesIntegers to output the integers #}
{% for value in valuesCars %}
    {% if counts[value] is not defined %}
        {% set counts = counts|merge({ (value): 1 }) %}
    {% else %}
        {% set counts = counts|merge({ (value): counts[value] + 1 }) %}
    {% endif %}
{% endfor %}

{% for key, count in counts %}
    {{ key }}: {{ count }}
{% endfor %}

For valuesCars I get the correct output:
bmw: 2
mercedes: 1
seat: 3
porsche: 1

For valuesIntegers I don't get the desired output. I get this:
0: 1
1: 1
2: 1
3: 1
4: 1
5: 1
6: 1

The desired output should be
27 : 2
32: 3
36 : 2

What am I'm missing here?


Solution

  • This is the solution:

    Convert the numeric key value into a string key to avoid issues with numeric indices

    {% set stringKey = 'value_' ~ value %}
    

    This results in:

    value_27
    value_32
    value_36
    value_36
    value_27
    value_32
    value_32

    Check if the key exists and update its count, else set the count to 1

    {% if attribute(valuesWithNewKey, stringKey) is defined %}
        {% set valuesWithNewKey = valuesWithNewKey|merge({ (stringKey): valuesWithNewKey[stringKey] + 1 }) %}
    {% else %}
        {% set valuesWithNewKey = valuesWithNewKey|merge({ (stringKey): 1 }) %}
    {% endif %}
    

    Inside the for loop (value in valuesIntegers) when you print the stringKey and values you will get:

    {% for value in valuesWithNewKey %}
        {{ value }}<br>
    {% endfor %}
    

    value_27 : 1
    value_32 : 1
    value_32 : 1
    value_36 : 1
    value_36 : 1
    value_36 : 1
    value_36 : 1
    value_36 : 1
    value_36 : 2
    value_27 : 2
    value_27 : 1
    value_27 : 2
    value_32 : 2
    value_32 : 2
    value_32 : 2
    value_32 : 2
    value_32 : 3
    value_32 : 2

    Outside the for loop (value in valuesIntegers) after, replacing the 'value_' it will result in the desired output 🙏 💪 🏆

    {% for stringKey, count in valuesWithNewKey %}
       {{ stringKey|replace({'value_':''}) ~ ' : ' ~ count }}<br>
    {% endfor %}
    

    27 : 2
    32 : 3
    36 : 2

    The whole code:

     {% set valuesIntegers = [27, 32, 36, 36, 27, 32, 32] %}
     {% set valuesWithNewKey = {} %}
    
    {% for value in valuesIntegers %}
       {% set stringKey = 'value_' ~ value %}
    
       {% if attribute(valuesWithNewKey, stringKey) is defined %}
         {% set valuesWithNewKey = valuesWithNewKey|merge({ (stringKey): valuesWithNewKey[stringKey] + 1 }) %}
       {% else %}
         {% set valuesWithNewKey = valuesWithNewKey|merge({ (stringKey): 1 }) %}
       {% endif %}
    {% endfor %}
    
    {% for stringKey, count in valuesWithNewKey %}
       {{ stringKey|replace({'value_':''}) ~ ' : ' ~ count }}<br>
    {% endfor %}