The Curious Case of the Undocumented Where Functionality

I was reading some chatter on the Shopify Partner Slack recently about the recently added liquid where filter. Anybody who’s used Jekyll before might be used to it already, but it was only added to the Shopify flavour of liquid a few months ago. It turns out that this filter has an undocumented use that’s quite handy.

Who, What, Where?

The where filter is used to filter arrays on objects. It takes 2 arguments: a property to check, and a value the property must match. For example, if you imagine that you’re creating a collection template for a clothes store, and you want to create a row of t shirts at the top, you could use the following snippet:

{% assign t_shirts = collection.products | where: 'type', 't shirt' %}
{% for t_shirt in t_shirts %}
  {% include 'featured-product', product: t_shirt %}
{% endfor %}

In this example, the t_shirts array will only contain products with a “type” of “t shirt”.

Great, so what’s the alternative method?

The discussion on Slack caught my eye, because somebody reported that they could check for the existence of a tag by passing only one argument. For example:

{% assign is_t_shirt = product.tags | where: 't-shirt' %}

Here, the where filter is just checking for the existence of t-shirt within the tags array. This isn’t extremely useful in and of itself, but it did spark my curiosity.

Often tags are used to determine whether or not to render certain elements in a template, or they will affect an element being rendered based on their value. An example of this is how tags might be used for colour swatches. The following isn’t uncommon, using the following tag pattern colour <colour>:

{% for tag in product.tags %}
  {% if tag contains 'colour ' %}
    {% assign swatch = tag | remove_first: 'colour ' %}
  {% endfor %}
{% endfor %}

{% if swatch %}
  {% include 'swatch' %}
{% endif %}

As it turns out, the where filter essentially has the same functionality as the for-if logic above. Using where, it can be changed to:

  assign swatch = product.tags
  | where: 'colour '
  | first
  | remove_first: 'colour '

Despite the product tag being colour orange, where will still match the tag, because it includes the string it’s searching for. If you exclude the first and remove_first filters, you can print the tag(s) it returns still, but it’s interesting to note.

When just one argument is passed to the filter, it doesn’t match strings exactly, instead it compares strings by checking to see if they contain the provided string. This means it can be used on other properties too, such as description, though I’m not aware of any great uses for those.

It’s worth noting though, the three filters provided in the example above must be used if you plan to retrieve a value from the tag. The where filter actually returns an array, though you might not realise if you don’t apply any other filters. By using remove_first:, the array then gets printed as if it’s a JSON array, hence, first must be used so that we are only modifying the value of the first array item, rather than the whole array. If more than one item is contained within the array, remove_first: will only remove the string from the first tag to contain it.

{%- assign swatch = product.tags | where: 'colour ' -%}

  prints "colour orange"

{%- assign swatch = product.tags | where: 'colour ' | remove_first: 'colour' -%}

  prints "["orange"]"

{%- assign swatch = product.tags | where: 'colour ' | first | remove_first: 'colour' -%}

  prints "orange"