You use collect to transform every item in a collection into a result collection; it is for MDriven developers writing OCL in a ViewModel, constraint, or debugger expression.
What collect does
collect iterates over an input collection and evaluates an expression once for each item. It returns a new collection containing the result from each evaluation.
For example, this expression reads every Customer and returns their names:
Customer.allInstances->collect(c | c.Name)
If the input contains three customers, the result contains three name values.
The collect operator does not itself change model state. The expression inside it can, however, call an operation or EAL statement that changes state. Use this deliberately: if your purpose is to perform an action rather than build a result collection, see Collect very slow for the implications and alternatives.
Syntax
collection->collect(item | expression)
| Part | Meaning |
|---|---|
collection
|
The collection to iterate over. |
item
|
The loop variable. It represents the current element of the collection. |
expression
|
The value to calculate for the current item. Each calculated value becomes an item in the returned collection. |
Transform values
Use collect when each source item should produce a derived value.
Customer.allInstances->collect(c | c.Age + 1)
This returns a collection where each element is a customer's age plus one. The Customer objects and their Age values are not changed.
You can also transform text that has been split into parts. The following expression splits a comma-separated string and removes surrounding whitespace from each part:
<string>.split(',')->collect(s | s.trim)
For the requirements and syntax of split, see Documentation:OCLOperators split. To turn a collection of strings into one comma-separated string, use asCommaList.
Use literal collections
You can use collect with a literal Set or Bag. A Set contains no duplicates; a Bag can contain duplicates.
Set{14,22,12,53,2,11,66}->collect(i | DoSomthingWithAnInt(i))
Here, i is each integer in the Set, and the result collection contains the value returned by DoSomthingWithAnInt(i) for each integer.
Execution context
You can evaluate collect in these contexts:
- ViewModel expressions
- Constraints
- Debugger → Evaluate Expression
- Debugger → Execute Action
collect is an OCL operator, not an EAL operator. In an executable context, the expression within collect may still invoke logic that changes state.
Avoid using collect only for side effects
Because collect always builds and returns a result collection, it can consume substantial memory and time when its inner expression returns a collection that grows during the iteration.
For example, this creates Detail objects and adds them to vCollectionDetails:
Sequence{0..7000}->collect(a | vCollectionDetails.add(Detail.Create))
The returned result is not a small status value. Since add returns the growing collection, collect builds a collection containing progressively larger collections. If you do not need that returned result, do not use collect as a convenient loop without considering the cost.
If you must retain this collect pattern, return a small value from the inner expression instead:
Sequence{0..7000}->collect(a | vCollectionDetails.add(Detail.Create); 0)
This returns a collection of zeroes rather than a collection of growing Detail collections. For the full performance explanation and guidance, see Documentation:Collect very slow.
Collect and state-changing expressions
The following expression uses collect to run an EAL remove operation for every selected order:
vSelected_Orders->collect(x | vCurrent_Customer.Orders.remove(x))
In this case, collect performs the iteration and returns the results of remove; remove is the operation that changes the customer's Orders association. Read Documentation:OCLOperators remove before using this pattern.
See also
- Documentation:OCLOperators Set
- Documentation:OCLOperators Bag
- Documentation:Collect very slow
- Documentation:OCLOperators split
- Documentation:OCLOperators asCommaList
- Documentation:OCLOperators remove
Flattening nested collection results
Flattening nested collection results
OCL automatically expands collections of collections into a single collection. This is also called flattening: the result is reduced from a collection of per-item collections to one collection.
For example, if each Thing has a Details collection:
Thing.allInstances->collect(t | t.Details)
The result is one collection containing the details from all Thing objects, rather than a collection containing one Details collection per Thing.
For more collection operator examples, see Documentation:Examples on collection operators.
