FEEL

FEEL list functions: list contains, sum, count, sort, filter and more

FEEL guide · 26 built-in functions · DMN 1.6

Lists are how a DMN decision deals with more than one of anything: order lines, applicants, past claims. FEEL lists are ordered, can hold values of any type including null and other lists, and are never modified. Every list function returns a new list.

This guide covers every list built-in, plus the patterns you need around them: summing a field across a list of contexts, filtering out nulls before aggregating, and when to use list contains and when in. Each result was computed by the FEEL engine behind the evaluator at the top.

All FEEL list functions at a glance

FunctionSignatureReturns
list containslist contains(list, element)Returns true if the list contains the element
countcount(list)Returns the number of elements in the list
minmin(list) or min(c1, …, cN)Returns the smallest element
maxmax(list) or max(c1, …, cN)Returns the largest element
sumsum(list) or sum(n1, …, nN)Returns the sum of the numbers
meanmean(list) or mean(n1, …, nN)Returns the arithmetic average of the numbers; null for an empty list
medianmedian(list) or median(n1, …, nN)Returns the median: the middle element of the sorted values, or the mean of the two middle elements for an even count
stddevstddev(list) or stddev(n1, …, nN)Returns the sample standard deviation of the numbers
modemode(list) or mode(n1, …, nN)Returns the most frequent value(s) as a list; ties produce several results in ascending order
allall(list) or all(b1, …, bN)Returns true if every element is true, false if any element is false, and null otherwise
anyany(list) or any(b1, …, bN)Returns true if at least one element is true, false if all are false, and null otherwise
sublistsublist(list, start position, length?)Returns the slice of the list starting at start position, running to the end or for length elements
appendappend(list, item…)Returns a new list with the items added to the end
concatenateconcatenate(list…)Returns one list containing the elements of all argument lists, in order
insert beforeinsert before(list, position, newItem)Returns a new list with newItem inserted before the given position
removeremove(list, position)Returns a new list with the element at the given position removed
reversereverse(list)Returns the list in reverse order
index ofindex of(list, match)Returns the positions of all occurrences of match, as an ascending list of 1-based indexes
unionunion(list…)Returns all elements of the argument lists with duplicates removed, preserving first-seen order
distinct valuesdistinct values(list)Returns the list with duplicate elements removed, preserving first-seen order
duplicate valuesduplicate values(list)Returns the values that appear more than once in the list
flattenflatten(list)Returns a flat list with all nested lists recursively expanded in place
productproduct(list) or product(n1, …, nN)Returns the product of the numbers
sortsort(list, precedes)Returns the list sorted by the precedes function
list replacelist replace(list, position | match, newItem)Returns a new list with the element at the position, or every element accepted by the matcher function, replaced by newItem
partitionpartition(list, size)Splits the list into consecutive chunks of the given size; the last chunk may be shorter

Rules every list function follows

Positions are 1-based, and negative positions count from the end. A position must exist in the list: 0 or anything beyond the length is outside the function's domain, and the result is null. The same goes for a sublist length that runs past the end of the list.

A single value counts as a one-element list. Where a function expects a list, FEEL converts a single value into a list with that one element, so count("abc") is 1 and sum(5) is 5.

Aggregates do not skip nulls. sum, mean, min, max and the others return null if any element is null. Filter the list first with [item != null].

sublist([1, 2, 3], -2)
[2, 3] Try it ↑

Negative start: the last two elements.

sublist([1, 2, 3], 5)

Out of range gives null.

sublist([1, 2, 3], 2, 10)

So does a length past the end.

count("abc")

A single value is treated as a one-element list.

sum([1, null, 3])

One null element makes the whole sum null.

sum([1, null, 3][item != null])

Filter out nulls first.

Summing and counting over a list of contexts

Decision inputs are usually lists of contexts, such as order lines with a price and a quantity. Selecting a field from a list of contexts gives the list of that field's values, so sum(lines.price) works directly. For anything more than one field, use a for expression, and add a filter to count only matching entries.

sum([{price: 2, qty: 3}, {price: 5, qty: 1}].price)

.price on a list of contexts gives the list of prices.

sum(for line in [{price: 2, qty: 3}, {price: 5, qty: 1}] return line.price * line.qty)

Line totals: price times quantity, summed.

count([{age: 17}, {age: 34}, {age: 52}][age >= 18])

Inside a filter, the fields of each context are in scope directly.

max([{age: 17}, {age: 34}, {age: 52}].age)

list contains, in and =

list contains(list, x) and the in operator both test membership, and both find a null element, because null = null is true. Neither converts types, so the string "2" is not found in a list of numbers. Numbers compare by value, so 1 and 1.0 are equal.

list contains([1, null, 3], null)
2 in [1, 2, 3]
null in [1, null, 3]
list contains([1, 2, 3], "2")

No conversion between strings and numbers.

list contains([1.0, 2], 1)

1 and 1.0 are the same number.

list contains([{a: 1}], {a: 1})

Contexts and lists compare by content.

Combining lists: append, concatenate, union, flatten

append adds its arguments as elements, so appending a list nests it. concatenate joins lists end to end. union also joins them but drops duplicates. flatten removes nesting at every depth.

append([1], [2])
[1, [2]] Try it ↑

The list becomes a single nested element.

concatenate([1], [2])
[1, 2] Try it ↑
union([1, 2], [2, 3])
[1, 2, 3] Try it ↑
flatten([1, [2, [3, [4]]]])
[1, 2, 3, 4] Try it ↑

Function reference

Each function with its examples and edge cases. The heading links to the function's own page with its parameter table.

list contains

list contains(list, element)

Returns true if the list contains the element. Unlike the = operator, it can also test for null.

The element may be null, so list contains also tests whether a list has a null entry. Comparison is by value with no type conversion.

list contains([1, 2, 3], 2)
list contains([1, null, 3], null)
list contains([1, 2, 3], "2")

A string is never equal to a number.

list contains([1.0, 2], 1)
list contains([[1, 2], [3]], [1, 2])

Nested lists compare by content.

list contains([date("2026-01-01")], date("2026-01-01"))
list contains([], 1)
list contains(null, 1)

A null list gives null in FEEL; B-FEEL returns false.

count

count(list)

Returns the number of elements in the list.

count([1, 2, 3])
count([])
count([1, null])

Null elements are counted.

count([[1, 2], 3])

A nested list counts as one element.

min

min(list) or min(c1, …, cN)

Returns the smallest element. All elements must be comparable with each other (numbers, strings, or temporal values).

min([1, 2, 3])
min("a", "b", "c")
min([])
min([1, "a"])

Mixed types cannot be compared.

max

max(list) or max(c1, …, cN)

Returns the largest element. All elements must be comparable with each other.

max(1, 2, 3)
max([date("2026-01-01"), date("2026-07-24")])
2026-07-24 Try it ↑
max([1, null, 3])

A null element cannot be compared. Filter nulls first.

max([1, null, 3][item != null])

sum

sum(list) or sum(n1, …, nN)

Returns the sum of the numbers. The sum of an empty list is null.

sum takes either one list or several numbers as separate arguments. The two things that surprise people are that an empty list sums to null, not 0, and that one null element makes the whole sum null. Both matter when the list comes from a filter that can match nothing.

To sum a field across a list of contexts, select the field: sum(orders.amount). To sum a computed value, use for.

sum([1, 2, 3])
sum([])
sum([1, 2][item > 5])

A filter that matches nothing gives an empty list, and its sum is null, not 0. B-FEEL returns 0.

sum([1, null, 3])
sum([1, null, 3][item != null])
sum([0.1, 0.2])

FEEL numbers are decimal, so there is no floating-point rounding error.

sum(for o in [{price: 2, qty: 3}, {price: 5, qty: 1}] return o.price * o.qty)
sum([duration("PT1H"), duration("PT30M")])

sum only adds numbers; durations give null.

mean

mean(list) or mean(n1, …, nN)

Returns the arithmetic average of the numbers; null for an empty list.

mean([1, 2, 3])
mean([])
mean([1, null])

Nulls are not skipped. B-FEEL ignores them and returns 1.

median

median(list) or median(n1, …, nN)

Returns the median: the middle element of the sorted values, or the mean of the two middle elements for an even count.

median(8, 2, 5, 3, 4)
median([6, 1, 2, 3])
median([])

stddev

stddev(list) or stddev(n1, …, nN)

Returns the sample standard deviation of the numbers.

stddev(2, 4, 7, 5)
2.081665999466132735282297706979931 Try it ↑
stddev([1])

The sample standard deviation needs at least two values.

mode

mode(list) or mode(n1, …, nN)

Returns the most frequent value(s) as a list; ties produce several results in ascending order.

mode(6, 3, 9, 6, 6)
mode([6, 1, 9, 6, 1])
[1, 6] Try it ↑
mode([])

Unlike the other aggregates, an empty list gives an empty list.

all

all(list) or all(b1, …, bN)

Returns true if every element is true, false if any element is false, and null otherwise. all([]) is true.

all uses three-valued logic. A single false makes the result false, even if there are nulls. Otherwise any null makes it null. Elements that are not booleans also give null.

all([true, true])
all([true, false])
all([])
all([true, null])
all([false, null])

False wins over null.

all([1])

Non-boolean elements give null.

any

any(list) or any(b1, …, bN)

Returns true if at least one element is true, false if all are false, and null otherwise. any([]) is false.

any([false, true])
any([])
any([true, null])

True wins over null.

any([null, false])

sublist

sublist(list, start position, length?)

Returns the slice of the list starting at start position, running to the end or for length elements.

sublist([4, 5, 6], 1, 2)
[4, 5] Try it ↑
sublist([4, 5, 6], -1)
sublist([1, 2, 3], 5)

A start position out of range gives null.

sublist([1, 2, 3], 0)

There is no position 0.

append

append(list, item…)

Returns a new list with the items added to the end.

append([1], 2, 3)
[1, 2, 3] Try it ↑
append([1], [2])
[1, [2]] Try it ↑

A list argument is added as one nested element. Use concatenate to join lists.

append([1], null)
[1, null] Try it ↑

Null is a valid element.

concatenate

concatenate(list…)

Returns one list containing the elements of all argument lists, in order.

concatenate([1, 2], [3])
[1, 2, 3] Try it ↑
concatenate([1], [2], [3])
[1, 2, 3] Try it ↑

insert before

insert before(list, position, newItem)

Returns a new list with newItem inserted before the given position.

insert before([1, 3], 1, 2)
[2, 1, 3] Try it ↑
insert before([1, 2], -1, 9)
[1, 9, 2] Try it ↑

Negative positions count from the end.

insert before([1, 2], 3, 9)

The position must exist, so this cannot append. Use append.

remove

remove(list, position)

Returns a new list with the element at the given position removed.

remove([1, 2, 3], 2)
[1, 3] Try it ↑
remove([1, 2, 3], -1)
[1, 2] Try it ↑

Removes the last element.

remove([1, 2, 3], 5)

reverse

reverse(list)

Returns the list in reverse order.

reverse([1, 2, 3])
[3, 2, 1] Try it ↑

index of

index of(list, match)

Returns the positions of all occurrences of match, as an ascending list of 1-based indexes.

index of([1, 2, 3, 2], 2)
[2, 4] Try it ↑
index of([1, 2], 3)

No match gives an empty list, not null or 0.

union

union(list…)

Returns all elements of the argument lists with duplicates removed, preserving first-seen order.

union([1, 2], [2, 3])
[1, 2, 3] Try it ↑

distinct values

distinct values(list)

Returns the list with duplicate elements removed, preserving first-seen order.

distinct values([1, 2, 3, 2, 1])
[1, 2, 3] Try it ↑

duplicate values

duplicate values(list)

Returns the values that appear more than once in the list.

duplicate values([1, 2, 3, 2, 1])
[1, 2] Try it ↑

flatten

flatten(list)

Returns a flat list with all nested lists recursively expanded in place.

flatten([[1, 2], [[3]], 4])
[1, 2, 3, 4] Try it ↑

product

product(list) or product(n1, …, nN)

Returns the product of the numbers.

product(2, 3, 4)
product([])
product([2, null])

sort

sort(list, precedes)

Returns the list sorted by the precedes function.

The precedes function decides the order: it receives two elements and returns true if the first belongs before the second. To sort contexts, compare the field you want. To sort in descending order, use >.

DMN 1.6 lists both parameters without marking precedes optional. The engine behind this site also accepts sort(list) for comparable elements and sorts them ascending, but a portable model passes the function.

sort([3, 1, 2], function(x, y) x < y)
[1, 2, 3] Try it ↑
sort(["b", "a", "c"], function(x, y) x < y)
[a, b, c] Try it ↑
sort([3, 1, 2], function(x, y) x > y)
[3, 2, 1] Try it ↑

Descending.

sort([{n: "b"}, {n: "a"}], function(x, y) x.n < y.n)
[{n=a}, {n=b}] Try it ↑

Sort contexts by a field.

list replace

list replace(list, position | match, newItem)

Returns a new list with the element at the position, or every element accepted by the matcher function, replaced by newItem.

list replace([2, 4, 7, 8], 3, 6)
[2, 4, 6, 8] Try it ↑
list replace([2, 4, 7, 8], function(item, newItem) item > 5, 0)
[2, 4, 0, 0] Try it ↑

partition

partition(list, size)

Not part of the DMN 1.6 standard: an extension supported by the engine behind this site. A model that uses it may not run on other DMN engines.

Splits the list into consecutive chunks of the given size; the last chunk may be shorter.

partition([1, 2, 3, 4, 5], 2)
[[1, 2], [3, 4], [5]] Try it ↑
partition([1, 2, 3], 0)

The size must be positive.

How B-FEEL differs

In standard FEEL an invalid argument makes the result null. The business-friendly B-FEEL dialect returns a type default instead. Both columns were evaluated by the same engine:

ExpressionFEELB-FEEL
sum([1, "2", 3])null4
index of(null, 1)null[]
sublist(null, 1)null[]

More FEEL guides

FEEL string functions · FEEL numeric functions · FEEL date and time functions · Language constructs · Examples cheat sheet

Defined by the OMG DMN 1.6 specification, chapter 10.3.4 (built-in functions).