Expression-variant array filters

The array query filters match on a single field equality. The _exp variants instead take an item-variable name and a predicate expression evaluated for each element, so the test can be any Liquid expression — comparisons, boolean logic, property paths, even nested filters. All are Pagelove/LiquidJS extensions.

Signature: collection | filter_exp: "<var>", "<expression>". The element is bound to <var> for the expression (as a {% for %}-loop variable would be), and the expression is a Liquid expression string.

Nested expression filters are supported and terminate normally, so a predicate may itself query a collection:

{{ groups | where_exp: "g", "g.items | has_exp: 'i', 'i.active'" }}

One limit applies: expression-filter evaluation may nest at most 32 levels deep. Beyond that the filter reports an error rather than continuing.

The limit exists because a predicate is re-resolved each time it is evaluated, so a predicate that refers to itself would otherwise recurse forever:

{% assign pred = "users | where_exp: 'x', pred" %}
{{ users | where_exp: "u", pred }}

That is refused. Ordinary nesting is nowhere near the limit — one or two levels is typical — so a template that hits it is almost certainly self-referential.

Where the error appears depends on context, as it does for every Liquid error: inside {{ }} output the page still renders and the failure appears in place as an error item; inside {% assign %} the error stops the render, so the page does not appear at all.

where_exp

The sub-array of items whose expression is truthy.

{{ users | where_exp: "u", "u.age >= 18" | map: "name" | join: ", " }}

reject_exp

The sub-array of items whose expression is falsy — the inverse of where_exp.

{% assign upcoming = events | reject_exp: "e", "e.cancelled" %}

find_exp

The first item whose expression is truthy (or nil).

{{ products | find_exp: "p", "p.price < 10" | map: "title" }}

find_index_exp

The index of the first item whose expression is truthy (or nil).

{{ steps | find_index_exp: "s", "s.done == false" }}

has_exp

A boolean — whether any item's expression is truthy.

{% if events | has_exp: "e", "e.starts > now" %}Upcoming events{% endif %}

group_by_exp

Groups by the value of the expression, returning { name, items } objects in first-seen key order. Because the key is an expression, you can group by a computed value — here, the year of each order:

{% assign by_year = orders | group_by_exp: "o", "o.date | date: '%Y'" %}
{% for group in by_year %}
  <h3>{{ group.name }}</h3>
  <ul>{% for order in group.items %}<li>{{ order.ref }}</li>{% endfor %}</ul>
{% endfor %}

See also