Twig is the template language used in Symfony and thousands of other projects. In the last six months alone, Twig has released 30 versions for its 1.x and 2.x branches, adding lots of interesting new features. This article focuses on some of the new filters and tags added recently.

Filter, map and reduce¶

The "filter, map and reduce" pattern is getting more and more popular in other programming languages and paradigms (e.g. functional programming) to transform collections and sequences of elements. You can now use them in Twig thanks to the new filter, map and reduce filters in combination with the new arrow function.

The filter filter removes from a sequence all the elements that don't match the given expression. For example, to ignore products without enough stock:

1 2 3 {% for product in related_products | filter ( product => product.stock > 10 ) %} {# ... #} {% endfor %}

The arrow function receives the value of the sequence or mapping as its argument. The name of this argument can be freely chosen and it doesn't have to be the same as the variable used to iterate the collection:

1 2 3 {% for product in related_products | filter ( p => p.id not in user.recentPurchases ) %} {# ... #} {% endfor %}

If you also need the key of the sequence element, define two arguments for the arrow function (and wrap them in parenthesis):

1 2 3 {% set components = all_components | filter ( ( v , k ) => v.published is true and not ( k starts with 'Deprecated' ) ) %}

Thanks to the new filter option, the if condition is no longer needed for the for loops, so we've deprecated it in favor of always using filter :

1 2 3 4 -{% for product in related_products if product.stock > 10 %} +{% for product in related_products|filter(p => p.stock > 10) %} {# ... #} {% endfor %}

If you prefer to keep using the for ... if pattern, include the if in the for loop:

1 2 3 4 5 {% for product in related_products %} {% if product.stock > 10 %} {# ... #} {% endif %} {% endfor %}

The map filter applies an arrow function to the elements of a sequence or a mapping (it's similar to PHP's array_map() ):

1 2 3 4 5 6 7 {% set people = [ { first : "Alice" , last : "Dupond" }, { first : "Bob" , last : "Smith" }, ] %} {{ people | map ( p => p.first ~ ' ' ~ p.last )| join ( ', ' ) }} {# outputs Alice Dupond, Bob Smith #}

The reduce filter iteratively reduces a sequence or a mapping to a single value using an arrow function. Because of this behavior, the arrow function always receives two arguments: the current value and the result of reducing the previous elements (usually called "carry"):

1 2 {% set num_products = cart.rows | reduce (( previousTotal , row ) => previousTotal + row.totalUnits ) %} {{ num_products }} products in total.