CASE: Sort associative arrays (simulate a dictionary/hashmap in liquid)

Hi there :raised_hand_with_fingers_splayed:,

You might have had the challenge of sorting an array that is associated/linked to another one. Unfortunately, we don’t have a way to create dictionaries in liquid so we can’t tell the language that those arrays have a relationship.

Let’s say you have these 2 arrays:

image

In our code we would have this:

{% assign values_array = values_array | split:'' %}
{% assign name_array = name_array | split:'' %}


{% push 'A' to:name_array %} {% push 500 to:values_array %}
{% push 'B' to:name_array %} {% push 300 to:values_array %} 
{% push 'C' to:name_array %} {% push 100 to:values_array %} 
{% push 'D' to:name_array %} {% push 500 to:values_array %}

Imagine you want to sort this table by values (lowest to highest). We can do so by using the sort filter in the following way:

{% assign sorted_values_array = values_array | sort %}

However if I want to now print both arrays we will see this:

{% comment %}Unsorted table{% endcomment %}

{% for sorted_value in sorted_values_array %}
  {{ name_array[forloop.index0] }} {{ sorted_value }}
{% endfor %}

image

As you can see, the values are sorted but the name_array remains static (i.e. A is 100 and not 500 as it should).

To solve this, we can create a workaround in liquid using a similar logic than the one used with vlook-ups in excel. First we run through the sorted_values_array and then we create a nested loop on the unsorted values_array that will look for the corresponding match:

{% comment %}Sorted table without index{% endcomment %}


{% for sorted_value in sorted_values_array %}
  {% for unsorted_value in values_array %}
    {% if sorted_value == unsorted_value %}
      {% assign name = name_array[forloop.index0] %}
      {% break %}
    {% endif %}
  {% endfor %}
  {{ name }} {{ sorted_value }} 
{% endfor %}

image

Now we see that the name_array match the values, but we still have an issue related to duplicate amounts as we have two amounts equal to 500. What the code does in both cases is finding the first 500 match which is A and prints it.
In order to solve this, we need to tell liquid that it has already printed that name and needs to keep looking. For that, we have to create an index array that saves the corresponding index value and checks whether it’s been already used:

{% comment %}Sorted table{% endcomment %}


{% assign index_array = index_array | split:'' %}

{% for sorted_value in sorted_values_array %}
  {% for unsorted_value in values_array %}
    {% assign get_name = true %}
    {% assign index = index+1 %}
    {% if sorted_value == unsorted_value %}
      {% for item in index_array %}
        {% if item == index %}
          {% assign get_name = false %}
          {% break %}
        {% endif %}
      {% endfor %}
      {% if get_name != false %}
        {% assign name = name_array[forloop.index0] %}
        {% push index to:index_array %}
        {% assign index = 0 %}
        {% break %}
      {% endif %}
    {% endif %}
  {% endfor %}
  {{ name }} {{ sorted_value }} 
{% endfor %}

image

The above is your final code to solve the dictionary limitation in liquid. We keep pushing the index value in the index_array, then we check whether this index is already there in order to avoid printing the same name and keep on looking for the next one.

I hope you find this useful!

Best,
Borja

1 Like