Built-ins for sequences - Apache FreeMarker Manual

Unsupported web browser - Use a modern browser to view this website!

- Home
- Manual
- Java API

- Contribute
- Report a Bug
- Download

Manual
Previous Next

# Built-ins for sequences

## chunk

This built-in splits a sequence into multiple sequences of the size given with the 1st parameter to the built-in (like `mySeq?chunk(3)`). The result is the sequence of these sequences. The last sequence is possibly shorter than the given size, unless the 2nd parameter is given (like `mySeq?chunk(3, '-')`), that is the item used to make up the size of the last sequence to the given size. Example:

Template

    <#assign seq = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']>

    <#list seq?chunk(4) as row>
      <#list row as cell>${cell} </#list>
    </#list>

    <#list seq?chunk(4, '-') as row>
      <#list row as cell>${cell} </#list>
    </#list>

The output will be:

Output

      a b c d
      e f g h
      i j

      a b c d
      e f g h
      i j - -

This built in is mostly for outputting sequnces in tabular/columnar format. When used with HTML tables, the 2nd parameter is often `"\xA0"` (that is the code of the no-break space character, also known as "nbsp"), so the border of the empty TD-s will not be missing.

The 1st parameter must be a number that is at least 1. If the number is not integer, it will be silently rounded down to integer (i.e. both 3.1 and 3.9 will be rounded to 3). The 2nd parameter can be of any type and value.

## drop_while

Returns a new sequence that contains the elements from the input sequence starting with from the first element that does not match the parameter predicate (condition). After that, all elements are included, regardless if they match the predicate or not. See the `filter` built-in for more details about parameters, and other details, but note that the condition in `filter` has opposite meaning (what to keep, instead of what to drop).

Example and comparison with `filter`:

Template

    <#assign xs = [1, 2, -3, 4, -5, 6]>

    Drop while positive:
    <#list xs?drop_while(x -> x > 0) as x>${x} </#list>

    Filer for positives:
    <#list xs?filter(x -> x > 0) as x>${x} </#list>

Output

    Drop while positive:
    -3 4 -5 6

    Filer for positives:
    1 2 4 6

As you can see, `drop_while` has stopped dropping the elements once it ran into the first element that didn't match the predicate (`x > 0`). On the other hand, `filter` keeps the elements that match the same predicate, and it doesn't stop.

See also: `take_while` built-in

## filter

Note:

This built-in is available since 2.3.29

Returns a new sequence that only contains the elements for which the parameter condition (the predicate) returns `true`. For example:

Template

    <#assign xs = [1, -2, 3, 4, -5]>
    Positives:
    <#list xs?filter(x -> x > 0) as x>${x} </#list>
    Negatives:
    <#list xs?filter(x -> x < 0) as x>${x} </#list>

Output

    Positives:
    1 3 4
    Negatives:
    -2 -5

This built-in has a single required parameter, the predicate (filter condition, what to keep). The predicate can be specified in 3 ways:

- As a single argument lambda expression: `element -> predicate`. In that, `element` is the variable name with which you can refer to the current element in the `predicate`, and the `predicate` is an arbitrarily complex expression that must return a boolean value (`true` or `false`). An example this was shown above. Note again the predicates can be arbitrarily complex, like the predicate in `products?filter(product -> product.discounted && !user.hasBought(product))`.

- As a function or method that has a single argument, and returns boolean. For example, the "Negatives" example above could be implemented like this:

  Template

      <#function negative(x)>
        <#return x < 0>
      </#function>

      ...

      Negatives:
      <#list xs?filter(negative) as x>${x} </#list>

  Note how we just referred to the function by name, and did not call it. Similarly, if you have a Java object called `utils` in the data-model, and it has a `boolean isNegative(Number n)` method, then you could use that like `xs?filter(utils.isNegative)`.

Note:

Remember, the condition (predicate) that you specify tells what to keep, not what to filter out! That is, the element will be in the result sequence when you return `true`, not when you return `false`. (It's like the `WHERE` condition in SQL, if you know that.)

While `filter` is most often used in the `list` directive, naturally it can be used anywhere where a filtered sequence is needed, and so this works as well:

Template

    <#assign negatives = xs?filter(x -> x < 0)>
    Negatives:
    <#list negatives as x>${x} </#list>

Note however, that for a very long sequences, the above solution can consume significantly more memory. That's because `<list seq?filter(pred) ...>` is optimized to do filtering without building an intermediate filtered sequence, while the n above example, `assign` will first build the whole filtered sequence in memory, and we pass that filtered sequence later to `list`. But again, this only matters for very long sequences.

See also: `take_while` built-in, `drop_while` built-in

### Lazy evaluation and its consequences

Note:

Identical rules apply to these built-ins as well: `map(mapper)`, `take_while(predicate)`, `drop_while(predicate)`.

To optimize processing, `filter` might delays fetching the elements of the input sequence, and applying the predicate on them. But it's guaranteed that those operations are not delayed past the point where the execution of the directive or interpolation, whose parameter contains the `seq?filter(predicate)`, is finished. Some examples:

- In the case of `<list seq?filter(predicate) ...>nested content</#list>`, when the execution enters the `nested content`, it's not true that all elements of `seq` was already consumed and filtered. Consuming and filtering `seq` is instead done bit by bit as `list` repeats the nested content. But it's guaranteed that past the `</#list>` tag (the end of the execution of the `list` directive), there are no delayed readings of `seq`, or delayed evaluation of the `predicate`. So avoid changing a such variable (or other system state) in the nested content of `list`, which influences the result of the `predicate`. Doing so could change the filtering for the rest of the `seq`.

- In the case of `<#assign filteredSeq = seq?filter(predicate)>` it's guaranteed that all elements of `seq` were processed, and thus `predicate` won't be evaluated after the `assign` directive.

- In the case of `${seq?filter(predicate)?join(', ')}` it's guaranteed that all elements of `seq` were processed, and thus `predicate` won't be evaluated after the `assign` directive.

Inside expressions however, there's no promise regarding when the elements are consumed and when the predicate is evaluated. Like in the case of `seq?filter(predicate1)?filter(predicate2)`, it's not guaranteed that `predicate1` will only be evaluated before `predicate2`. (Most likely they will be called alternately: `predicate1` for the 1st element, then `predicate2` for the 1st element, then `predicate1` for the 2nd element, then `predicate2` for the 2nd element, and so on.)

If you pass a filtered sequence to a custom directive (a macro) or function or method, as in `<@myMacro seq?filter(predicate) />` or `myFunction(seq?filter(predicate))`, then it's guaranteed that the filtering is not delayed past the point when the custom directive/function/method is invoked. That is, your macro/function/method will aways receive a fully constructed filtered sequence.

Also note that in it's not guaranteed that all elements of the input sequence will be read, and therefore that the predicate will be evaluated for all elements. Some examples of such cases:

- You may `break` out from `<list seq?filter(predicate) ...>` before it reaches the last element, in which case the rest of the `seq` elements won't be fetched and filtered.

- In the case of `seq?filter(predicate)[2]`, which reads the 3rd element of the filtered sequence, FreeMarker stops fetching and filtering the elements of `seq` when we have found the 3rd element that matches the `predicate`.

- In the case of `seq?filter(predicate)?size != 0`, which tells whether the filtered sequence is non-empty, we stop fetching and filtering the elements of `seq` when we have found the 1st element that matches the `predicate`. (That's certainly surprising as `?size` needs to process the whole sequence to tell the size. But in this case FreeMarker notices that we don't really need the exact size.)

If you are a Java programmer, note how the `filter` built-in differs from Java `Stream.filter`. `Stream.filter` is "lazy", while FreeMarker `filter` is basically "eager", and is only "lazy" in special cases, and within a limited scope. Thus, unlike in Java, calling `filter` is not always free. In particular, if you assign a filtered sequence to a variable, or pass it to a custom directive/function/method, the filtered sequence will be created eagerly.

### Filtering very long input that you don't hold in memory

Note:

Identical rules apply to these built-ins as well: `map(mapper)`, `take_while(predicate)`, `drop_while(predicate)`.

Some applications, particularly those that render huge tables, use sequence-like values in the data-model that are not held in memory at once, instead they are like a stream of elements that you can only read in the order as they are given to you (on the Java side these are `java.util.Iterator`-s, or `java.util.Iterables`, or the like). These will have "collection" type in the template language, which is like a restricted sequence.

`filter` works with collection input too. As you have seen earlier, `filter` might stores the entire filtered sequence in the memory, which in this case sounds concerning, because if the input was too big to fit into the memory (hence it wasn't exposed as a sequence), then the filtered collection can be too big as well. For that reason, if the input is not a sequence (but a collection), `filter` never collects its result into the memory, and never fetches and processes the input elements until they are really needed ("lazy" behavior). Furthermore the result of `filter` is then a collection, not a sequence, therefor sequence operations (like `seq[index]`) will not work on it.

Unlike with sequence input, any operation that would cause collecting the whole filtered result into the memory will now fail. Let's see that through examples. Let's say we have `hugeTable` in the data-model, which is not a sequence, but still a collection (probably an `Iterator` in Java). Then, consider:

Template

    <#-- Works: -->
    <#list hugeTable?filter(predicate) as row>nested content</#list>

This works fine, since `list` doesn't require collecting the result into the memory

Consider this:

Template

    <#-- Fails if hugeTable is not a sequence, just a collection: -->
    <#assign filteredHugeTable = hugeTable?filter(predicate)>

This fails, as filtering can't be postponed beyond the containing directive (`assign`), so FreeMareker had to put the entire filtered result into `filteredHugeTable`. If, however, you know that `filteredHugeTable` won't be too big, you can explicitly collect the result into a sequence via the `sequence` built-in:

Template

    <#-- Works, but be sure filtredHugeTable fits into the memory: -->
    <#assign filteredHugeTable = hugeTable?filter(predicate)?sequence>

Naturally, applying the `sequence` built-in allows all sequence operations, such as `seq[index]`, `seq[range]`, or `seq?size`. If these operations are directly applied on a sequence that was converted from a collection, then FreeMarker optimizes out actually creating the sequence in memory. So these won't consume much memory regardless of the size of the filtered `hugeTable`:

- `hugeTable?filter(predicate)?sequence[index]`: FreeMarker will just fetch and drop the elements till it reaches the element at the desired position.

- `hugeTable?filter(predicate)?sequence[0..9]`: FreeMarker will just collect the first 10 elements.

- `hugeTable?filter(predicate)?sequence?size`: In this case the whole `hugeTable` will be fetched, which is possibly slow, but the fetched elements are still not collected into the memory, as they only need to be counted.

### Filtering missing (null) values

The argument to a lambda expression can hold the missing (Java `null`) value, and reading such value will not fall back to a higher scope. Thus, something like `seq?filter(it -> it??)`, which filters out missing element from the sequence, will work reliably.

## first

Returns the first item of the sequence. Thus `value?first` is the same as `value[0]`, except that, since FreeMarker 2.3.26, `value?first` also works if `value` doesn't support getting items with numerical index, but still supports to be listed (i.e., with FTL collection values).

If the sequence or collection is empty, the result will be a missing value (as in `empty?first!'No item was found'`).

## join

Concatenates the items of a sequence to a single string, with the given separator. For example:

Template

    <#assign colors = ["red", "green", "blue"]>
    ${colors?join(", ")}

will output:

Output

    red, green, blue

Sequence items that are not strings will be converted to string with the same conversion rules as of `${...}` (except, of course, no automatic escaping is applied at this stage).

`?join(...)` can have up to 3 parameters:

1. Separator, required: The string that is inserted between items

2. Empty value, defaults to `""` (empty string): The value used if the sequence contains no items.

3. List ending, defaults to `""` (empty string): The value printed after the last value, if the list sequence wasn't empty.

So this (where `[]` means an empty sequence):

Template

    ${colors?join(", ", "-")}
    ${[]?join(", ", "-")}

    ${colors?join(", ", "-", ".")}
    ${[]?join(", ", "-", ".")}

will output:

Output

    red, green, blue
    -

    red, green, blue.
    -

Sequences coming from Java might contain `null` values. Those values will be ignored by this built-in, exactly like if they were removed from the list.

## last

The last subvariable of the sequence. Template processing will die with error if the sequence is empty.

## map

Returns an new sequence where all elements are replaced with the result of the parameter lambda, function, or method. For example, you have a list of user objects in `users`, but instead you need a list of user names in variable, then you could do this:

Template

    <#assign userNames = users?map(user -> user.name)>

The parameter work like the parameter of the with `filter` built-in (so see there), except that the lambda/function/method you specify can return values of any type.

Regarding lazy evaluation, and handling of very long inputs, it also works on the same way as the `filter` built-in.

## min, max

Returns the smaller (`min`) or greatest (`max`) item of the sequence (or collection). The items must be either all numbers, or all date/time values of the same kind (date-only, time-only, date-time), or else a comparison error will occur. These are the same restrictions as for the `<` and `>` operators.

Missing items (i.e., Java `null`-s) will be silently ignored. If the sequence is empty or it only contains missing (Java `null`) items, the result itself will be missing.

Example:

Template

    ${[1, 2, 3]?min}
    ${[1, 2, 3]?max}
    ${[]?min!'-'}

Output

    1
    3
    -

## reverse

The sequence with reversed order.

## seq_contains

Note:

The `seq_` prefix is required in the built-in name to differentiate it from the `contains` built-in that searches a substring in a string (since a variable can be both string and sequence on the same time).

Tells if the sequence contains the specified value (according the `==` operator of the template language, not according Java's `Object.equals`). It has 1 parameter, the value to find. Example:

Template

    <#assign x = ["red", 16, "blue", "cyan"]>
    "blue": ${x?seq_contains("blue")?string("yes", "no")}
    "yellow": ${x?seq_contains("yellow")?string("yes", "no")}
    16: ${x?seq_contains(16)?string("yes", "no")}
    "16": ${x?seq_contains("16")?string("yes", "no")}

The output will be:

Output

    "blue": yes
    "yellow": no
    16: yes
    "16": no

To find the value the built-in uses FreeMarker's comparison rules (as if you was using `==` operator), except that comparing two values of different types or of types for which FreeMarker doesn't support comparison will not cause error, just will be evaluated as the two values are not equal. Thus, you can use it only to find scalar values (i.e. string, number, boolean or date/time values). For other types the result will be always `false`.

For fault tolerance, this built-in also works with collections.

## seq_index_of

Note:

This built-in is available since FreeMarker 2.3.1. It doesn't exist in 2.3.

Note:

The `seq_` prefix is required in the built-in name to differentiate it from the `index_of` built-in that searches a substring in a string (since a variable can be both string and sequence on the same time).

Returns the index of the first occurrence of a value in the sequence, or `-1` if the sequence doesn't contain the specified value. The value to find is specified as the first parameter. For example this template:

Template

    <#assign colors = ["red", "green", "blue"]>
    ${colors?seq_index_of("blue")}
    ${colors?seq_index_of("red")}
    ${colors?seq_index_of("purple")}

will output this:

Output

    2
    0
    -1

To find the value the built-in uses FreeMarker's comparison rules (as if you was using `==` operator), except that comparing two values of different types or of types for which FreeMarker doesn't support comparison will not cause error, just will be evaluated as the two values are not equal. Thus, you can use it only to find scalar values (i.e. string, number, boolean or date/time values). For other types the result will be always `-1`.

The index where the searching is started can be optionally given as the 2nd parameter. This may be useful if the same item can occur for multiple times in the same sequence. There is no restriction on the numerical value of the second parameter: if it is negative, it has the same effect as if it were zero, and if it is greater than the length of the sequence, it has the same effect as if it were equal to the length of the sequence. Decimal values will be truncated to integers. For example:

Template

    <#assign names = ["Joe", "Fred", "Joe", "Susan"]>
    No 2nd param: ${names?seq_index_of("Joe")}
    -2: ${names?seq_index_of("Joe", -2)}
    -1: ${names?seq_index_of("Joe", -1)}
     0: ${names?seq_index_of("Joe", 0)}
     1: ${names?seq_index_of("Joe", 1)}
     2: ${names?seq_index_of("Joe", 2)}
     3: ${names?seq_index_of("Joe", 3)}
     4: ${names?seq_index_of("Joe", 4)}

will output this:

Output

    No 2nd param: 0
    -2: 0
    -1: 0
     0: 0
     1: 2
     2: 2
     3: -1
     4: -1

## seq_last_index_of

Note:

This built-in is available since FreeMarker 2.3.1. It doesn't exist in 2.3.

Note:

The `seq_` prefix is required in the built-in name to differentiate it from the `last_index_of` built-in that searches a substring in a string (since a variable can be both string and sequence on the same time).

Returns the index of the last occurrence of a value in the sequence, or `-1` if the sequence doesn't contain the specified value. That is, it is the same as `seq_index_of`, just it searches backward starting from the last item of the sequence. It also supports the optional 2nd parameter that specifies the index where the searching is started. For example:

Template

    <#assign names = ["Joe", "Fred", "Joe", "Susan"]>
    No 2nd param: ${names?seq_last_index_of("Joe")}
    -2: ${names?seq_last_index_of("Joe", -2)}
    -1: ${names?seq_last_index_of("Joe", -1)}
     0: ${names?seq_last_index_of("Joe", 0)}
     1: ${names?seq_last_index_of("Joe", 1)}
     2: ${names?seq_last_index_of("Joe", 2)}
     3: ${names?seq_last_index_of("Joe", 3)}
     4: ${names?seq_last_index_of("Joe", 4)}

will output this:

Output

    No 2nd param: 2
    -2: -1
    -1: -1
     0: 0
     1: 0
     2: 2
     3: 2
     4: 2

## size

The number of sub variables in sequence (as a numerical value). The highest possible index in sequence `s` is `s?size - 1` (since the index of the first subvariable is 0) assuming that the sequence has at least one subvariable.

## sort

Returns the sequence sorted in ascending order. (For descending order use this and then the `reverse` built in.) This will work only if all sub variables are strings, or if all sub variables are numbers, or if all sub variables are date values (date, time, or date+time), or if all sub variables are booleans (since 2.3.17). If the sub variables are strings, it uses locale (language) specific lexical sorting (which is usually not case sensitive). For example:

Template

    <#assign ls = ["whale", "Barbara", "zeppelin", "aardvark", "beetroot"]?sort>
    <#list ls as i>${i} </#list>

will print (with US locale at least):

Output

    aardvark Barbara beetroot whale zeppelin

## sort_by

Returns the sequence of hashes sorted by the given hash subvariable in ascending order. (For descending order use this and then the `reverse` built in.) The rules are the same as with the `sort` built-in, except that the sub variables of the sequence must be hashes, and you have to give the name of a hash subvariable that will decide the order. For example:

Template

    <#assign ls = [
      {"name":"whale", "weight":2000},
      {"name":"Barbara", "weight":53},
      {"name":"zeppelin", "weight":-200},
      {"name":"aardvark", "weight":30},
      {"name":"beetroot", "weight":0.3}
    ]>
    Order by name:
    <#list ls?sort_by("name") as i>
    - ${i.name}: ${i.weight}
    </#list>

    Order by weight:
    <#list ls?sort_by("weight") as i>
    - ${i.name}: ${i.weight}
    </#list>

will print (with US locale at least):

Output

    Order by name:
    - aardvark: 30
    - Barbara: 53
    - beetroot: 0.3
    - whale: 2000
    - zeppelin: -200

    Order by weight:
    - zeppelin: -200
    - beetroot: 0.3
    - aardvark: 30
    - Barbara: 53
    - whale: 2000

If the subvariable that you want to use for the sorting is on a deeper level (that is, if it is a subvariable of a subvariable and so on), then you can use a sequence as parameter, that specifies the names of the sub variables that lead down to the desired subvariable. For example:

Template

    <#assign members = [
        {"name": {"first": "Joe", "last": "Smith"}, "age": 40},
        {"name": {"first": "Fred", "last": "Crooger"}, "age": 35},
        {"name": {"first": "Amanda", "last": "Fox"}, "age": 25}]>
    Sorted by name.last:
    <#list members?sort_by(['name', 'last']) as m>
    - ${m.name.last}, ${m.name.first}: ${m.age} years old
    </#list>

will print (with US locale at least):

Output

    Sorted by name.last:
    - Crooger, Fred: 35 years old
    - Fox, Amanda: 25 years old
    - Smith, Joe: 40 years old

## take_while

Returns a sequence that only contains the elements of the input sequence which are before the first element that doesn't match the parameter predicate (filter condition). This is very similar to the `filter` built-in, so see further details there.

Example and comparison with `filter`:

Template

    <#assign xs = [1, 2, -3, 4, -5, 6]>

    Take while positive:
    <#list xs?take_while(x -> x > 0) as x>${x} </#list>

    Filer for positives:
    <#list xs?filter(x -> x > 0) as x>${x} </#list>

Output

    Take while positive:
    1 2

    Filer for positives:
    1 2 4 6

As you can see, `take_while` has stopped at the first number that didn't match the predicate (`x > 0`), while `filter` has continued finding further matches.

See also: `drop_while` built-in

Previous Next