Collections Expressions (DMN Compatible) - Gnorion/BizVR GitHub Wiki
- Creating Named and Dynamic Collections
- Decision Tables and Collections
- Processing Collections With Decision Tables
- Sep 2015 Collections of Cars
- Tutorial Testing with a collection of inputs

There are three basic collections.
- The basic collection is often called a bag. It stores objects with no ordering of the objects and no restrictions on them.
- Another unstructured collection is a set where repeated objects are not permitted: it holds at most one copy of each item. A set is often from a predefined universe.
- A collection where there is an ordering is often called a list. Specific examples include an array, a vector and a sequence.
- Collections are a bunch of objects that can be named and referred to.
- Different types of object can be grouped into a single collection.
- Objects in a collection can be basic data types, or structured data types or even collections.
- Special operations are available for processing collections
- The members of a collection do not even have to be of the same type.
example
["red","red",3.24,"2022/12/31",true,[1,2,["a","b",5]],"red"]is a collection
- Sets are collections of objects that contain no duplicates
- The objects in a set are called its elements or members.
- The elements in a set can be any types of objects, including sets!
- The members of a set do not even have to be of the same type.
Example
["red",3.24,"2022/12/31",true,[1,2,["a","b",5]]]is a set
- Lists are collections of objects that have been arranged into a specific sequence
- A collection can be sequenced on one or more attributes using the sort function
- An operation can return any data type including a collection.
Multiple collections can be joined and filtered.
| Example | Explanation |
|---|---|
for c in ["red","green"], n in [1,2] return [c,n] |
would generate a set of cross products of 4 tuples of the form [color,number]. i.e. [["red",1], ["red",2], ["green",1 ], ["green",2]]
|
for p in Persons, c in Cars where p.colorPref=c.color |
would produce a collection of tuples [p,c] where the car has a color the person prefers |
Example: Generating cross products Suppose you have a collection of persons such as these:
{
"persons": [
{"name":"Tom","age":57,"salary":100000,"gender":"male"},
{"name":"Dick","age":26,"salary":80000,"gender":"male"},
{"name":"Harry","age":37,"salary":160000,"gender":"male"},
{"name":"Jane","age":42,"salary":120000,"gender":"female"},
{"name":"Mary","age":26,"salary":180000,"gender":"female"},
{"name":"Ruth","age":76,"salary":180000,"gender":"female"}
]
}
| Business Question | Possible Implementation | Return Value |
|---|---|---|
| Is there any person over 65? | count(persons[age>65])>0 |
true |
| List any persons between 13 and 19? | persons [age in [13..19]] |
[] |
| Are there more than 2 persons between 20 and 65? | count(persons [age in [20..65]]) > 2 |
true |
| How many persons between 20 and 65? | {n:count(persons [age in [20..65]])} |
sets the value of n to 5 |
| List males under 40 | persons [age<40 and gender="male"] |
[{"name": "Dick","age": 26,"salary":80000,"gender": "male"},{"name": "Harry","age": 37,"salary": 160000,"gender": "male"}] |
| List the ages of males under 40 | persons [age<40 and gender="male"].age] |
[26,37] |
| Are all the people over 65 female? | all(persons [age>65].gender="female") |
true |
| Are there any males over 65 ? | notEmpty({p in Person where age>65,gender='male'}) |
returns T or F |
| Are there any non-males over 65? | notEmpty({p in Person where age>65,gender<>'male'}) |
|
| What is the highest income for males over 65? | highest={p in Person where age>65,gender='male'}.income.max |
|
| Is the youngest person over 65 a male? | {p in Person where age>65}.sortedBy(age).first.gender = 'male' |
returns T or F |
| What is the sum of incomes for people over 65 | incomeSum={p in Person where age>65}.income.sum |
returns the sum of incomes |
| Is the lowest income for over 65s greater than zero? | {p in Person where age>65}.income.min >0 |
returns T or F |
| What is the average age of the oldest 3 males between 21 and 45? | avgAge={p in Person where age in [20..65],gender='male'}.sortedBy(age).first(3).age.avg |
returns a number |
| Which are the oldest 3 males between 21 and 45? | oldest={p in Person where age in [20..65],gender='male'}.sortedBy(age).first(3) |
collection saved for later use |
| What is the sum of the incomes for the oldest N males? | incomeSum={p in Person where gender='male'}.sortedByDesc(age).first(N).income.sum |
returns a number |
| What state does the youngest person over 65 live in? | state={p in Person where age>65}.sortedBy(age).first.state |
|
| Find all the males over 65. | seniorMales={p in Person where age>65,gender='male'} |
collection saved for further use |
| Then find their average income. | seniorMaleAvgIncome=seniorMales.income.avg |
result is a value |
| Is the highest income of the oldest 5 persons greater than 10000? | {p in Person}.sortedBy(age).first(5).sortedByDesc(income).first.income>10000 |
|
| Is person with the lowest income over parm.x a parm.gender | {p in Person where income>parm.x}.sortedBy(income).first.gender=parm.gender |
|
| Is there a male over 65? | {p in Person where age>65}.exists(gender='male') |
returns true or false |
| or alternatively | {p in Person}.exists(gender='male',age>65) |
returns true or false |
| or alternatively | {p in Person where gender='male',age>65}.size>0 |
returns true or false |
| . | ||
| Find the sum of the two lowest book prices |
lowest_two=Books.sortedBy(price).at(1).price+Books.sortedBy(price).at(2).price returns a number |
|
| or | lowest_two=Books.sortedBy(price).first(2).price.sum |
|
| or | lowest_two=Books.sortedBy(price).subSequence(1,2).price.sum |
NOTE
avgAge={p in Person where age in [20..65],gender='male'}.sortedBy(age).first(3).age.avg can be expressed as a series of statements:
adultMales={p in Person where age in [20..65],gender='male'}adultMalesAscendingAge=adultMales.sortedBy(age)youngestThreeAdultMales=adultMalesAscendingAge.first(3)avgAge=youngestThreeAdultMales.age.avg
Person.iterate() Eg {p in Person}.iterate(p.net=p.gross-p.taxes) updates p.net for all persons
size, iterator(), add() or +=, remove() or -=, clear
| Operation | Example | Result |
|---|---|---|
| .exists | {p in Person where age>65}.exists(gender='male') |
true if any p over 65 is male |
| .forall | {p in Person where age>65}.forAll(gender='male') |
true if all p over 65 are male |
| .isEmpty | {p in Person where age>65}.isEmpty |
true if there are no p over 65 |
| .notEmpty | {p in Person where age>65}.notEmpty |
true if there is at least one p over 65 |
| .size | {p in Person where age>65}.size |
returns the number of p over 65 |
| .sortedBy | {p in Person where age>65}.sortedBy(age) |
the set of p over 65 in ascending age |
| .sortedByDesc | {p in Person where age>65}.sortedByDec(age) |
the set of p over 65 in descending age |
Collection must be sorted eg sortedCollection=collection.sortedBy(age)
| Operation | Example | Return |
|---|---|---|
| .at(n) | {p in Person where age>65}.sortedBy(age).at(1) |
returns the youngest p over 65 |
| .first(n) | {p in Person where age>65}.sortedBy(age).first |
returns the youngest p over 65 |
| .last(n) | {p in Person where age>65}.sortedBy(age).last(2) |
returns the oldest 2 p over 65 |
| .subSequence(i,j) | {p in Person}.sortedBy(age).subSequence(2,Person.size-1) |
returns all but the youngest and oldest p |
Assume A={red,green,blue} and B={red,yellow,blue,purple} where A and D are sets of instances of the same type
| Operation | Example | Result | Explain |
|---|---|---|---|
| union | C=A+B |
C={red,green,yellow,blue,purple} | what is in A and B combined. A+B=B+A
|
| union (alt) | union(A,B |
||
| intersection | C=A@B |
C={red,blue} | what is common to both A and B. Note A@B=B@A
|
| intersection (alt) | intersection(A,B) |
||
| difference | C=A-B |
C={green} | what is in A but not B. A-B<>B-A
|
| difference | C=B-A |
C={yellow,purple} | what is in B but not A. |
| difference | C=(A+B)-(A@B) |
C={yellow,green,purple} | what is not in both A and B. Also C=(A-B)+(B-A)
|
| difference | C=(A@B)-(A+B) |
C={} | always the empty set. |
| difference (alt) | difference(A,B) |
Each should implement a common interface Common attributes (if any) must be of the same type. Common methods (if any) may have different implementations but must return the same data type
| Operation | Example | Explain |
|---|---|---|
| union | transportation=cars+boats+planes |
merges three sets of different types into a single set with the combined attributes of all of them (effectively dynamic multiple inheritance) |
| union | transportation={c in Cars where seats>4,insured=True}+(b in boats where type='hovercraft',seaworthy=True})+{p in Planes where age<5} |
merges three sets with filters |
| Operation | Example | Notes |
|---|---|---|
| .sum | {1..100}.sum |
Sum of the first 100 integers |
| .sum | {N..M}.sum |
Sum of the integers from N to M |
| .sum | {n in 1..N where n.mod(2)=0}.sum |
Sum of the even numbers <= N |
| .sum | {p in Person where age>65}.income.sum |
Sum of incomes of people over 65 |
| .product | {1..100}.product |
Product of the first 100 numbers |
| .max | {p in Person where age>65}.income.max |
Largest income of people over 65 |
| .min | {p in Person where age>65}.income.min |
Smallest income of people over 65 |
| .avg | {p in Person where age>65}.income.avg |
Average income of people over 65 |
| .allContain() | {p in Person where age>65}.gender.allContain('male') |
equivalent to Person.forAll(age>65,gender='male')
|
| .uniqueCount() | {p in Person where age in [18..65]}.gender.uniqueCount |
Number of unique genders in persons 18..65 |
[list of any valid expression involving attributes of the collection that evaluates to boolean]
{p in Person where age>65} just those instances where age >65.
Person is equivalent to Person[] and Person[*] meaning every instance of the class with no filtering.
Person[n] any n arbitrary instances (can't think why we'd need this yet)
{p in Person where age>65,gender='male'} allows the filters to be applied in any order (perhaps even in parallel with the final result being the intersection of the two collections.
This is equivalent to {{p in Person where age>65} where gender='male'} which requires the age filter to be applied first and then the gender filter is applied to the results. Not sure if it makes a huge difference