Collection Types
The Collection Types
There are five collection types. Four of them—the Set, OrderedSet, Bag, and Sequence types—are concrete types and can be used in expressions. The fifth, the Collection type, is the abstract supertype of the other four and is used to define the operations common to all collection types.
The four concrete collection types are defined as follows:
- A Set is a collection that contains instances of a valid OCL type. A set does not contain duplicate elements; any instance can be present only once. Elements in a set are not ordered.
- An OrderedSet is a set whose elements are ordered.
- A Bag is a collection that may contain duplicate elements. A bag is typically the result of combining navigations. Elements in a bag are not ordered.
- A Sequence is a bag whose elements are ordered.
(Note that a value of type Sequence or OrderedSet is ordered and not sorted.)Set { 1 , 2 , 5 , 88 }
Set { 'apple' , 'orange', 'strawberry' }
OrderedSet { 'apple' , 'orange', 'strawberry', 'pear' }
Sequence { 1, 3, 45, 2, 3 }
Sequence { 'ape', 'nut' }
Bag {1 , 3 , 4, 3, 5 }
Sequence{ 1..(6 + 4) }
Sequence{ 1..10 }
-- are both identical to
Sequence{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
Collection Type Expressions
When the type of an element is a collection, this can be indicated using the words Set, OrderedSet, Bag, or Sequence, and the type of the elements of the collection between rounded brackets, as shown in the following examples:Set(Customer)
Sequence(Set(ProgramPartners))
OrderedSet(ServiceLevel)
Bag(Burning)
Collection Operations
context LoyaltyProgram
inv: self.participants->size()
Treating Instances as Collections
Because the OCL syntax for applying collection operations is different from that for user-defined type operations, you can use a single instance as a collection. This collection is considered to be a set with the instance as the only element.
As an instance: | As a collection: |
context Membership | context Membership |
Collections of Collections
When a collection is inserted into another collection, the resulting collection is automatically flattened; the elements of the inserted collection are considered direct elements of the resulting collection.Set {Set{1,2}, Set{3,4}, Set{5,6}}
(flattened)
Set { 1, 2, 3, 4, 5, 6 }
Operations on Collection Types
Standard operations on all collection types
In the invariant, you specify that the actual service level of a membership must be one of the service levels of the program to which the membership belongs:context Membership
inv: programs.levels ->includes(currentLevel)
The following invariant specifies that the available services for a service level must be offered by a partner of the loyalty program to which the service level belongs:context ServiceLevel
inv:program.partners->includesAll(self.availableServices.partner)
Operations with Variant Meaning
Collection operations with variant meaning
Set {Set{1,2}, Set{2,3}, Set{4,5,6}}
Set { 1, 2, 3, 4, 5, 6 }
(flatten)Bag {Set{1,2}, Set{1,2}, Set{4,5,6}}
Bag { 1, 1, 2, 2, 4, 5, 6 }
(flatten)Sequence {Set{1,2}, Set{2,3}, Set{4,5,6}}
Sequence { 2, 1, 2, 3, 5, 6, 4 }
(flatten)Set{1,4,7,10} - Set{4,7} = Set{1,10}
OrderedSet{12,9,6,3} - Set{1,3,2} = OrderedSet{12,9,6}
Set{1,4,7,10}.symmetricDifference(Set{4,5,7}) = Set{1,5,10}
Operations on OrderedSets and Sequences Only
Sequence{'a','b','c','c','d','e'}->first()= 'a'
OrderedSet{'a','b','c','d'}->last() = 'd'
Sequence{'a','b','c','c','d','e'}->at(3)= 'c'
Sequence{'a','b','c','c','d','e'}->indexOf( 'c' ) = 3
OrderedSet{'a','b','c','d'}->insertAt(3,'X’)= OrderedSet{'a','b','X','c','d'}
Sequence{'a','b','c','c','d','e'}-> subSequence(3,5)= Sequence{'c','c','d'}
OrderedSet{'a','b','c','d'}->subOrderedSet( 2, 3 ) = OrderedSet{'b','c'}
Sequence{'a','b','c','c','d','e'}-> append('X’)= Sequence{'a','b','c','c','d','e','X'}
Sequence{'a','b','c','c','d','e'}-> prepend('X’)= Sequence{'X','a','b','c','c','d','e'}
Loop Operations or Iterators
Loop operations on all collection types
Iterator Variables
context LoyaltyProgram
inv: self.Membership.account->isUnique(accacc.number)
context LoyaltyProgram
inv: self.Membership.account->isUnique(acc: LoyaltyAccountacc.number)
context LoyaltyProgram
inv: self.Membership.account->isUnique(number)
The sortedBy Operation
context LoyaltyProgram
def: sortedAccounts:Sequence(LoyaltyAccount) = self.Membership.account->sortedBy(number)
The select Operation
context CustomerCard
inv: self.transactions->select(points>100)->notEmpty()
element = collection.firstElement();
while(collection.notEmpty()) do
if()
then
result.add(element);
endif
element = collection.nextElement();
endwhile
return result;
The reject Operation
context Customer
inv: Membership.account->select(points>0)
context Customer
inv: Membership.account-> reject(not(points>0))
The any Operation
self.Membership.account->any(number<10000)
The forAll Operation
context LoyaltyProgram
inv: participants->forAll(age()<=70)
context LoyaltyProgram
inv: self.participants->forAll(c1 ▏self.participants->forAll( c2 ▏c1 <> c2 implies c1.name <> c2.name ))
context LoyaltyProgram
inv: self.participants->forAll(c1, c2 ▏c1 <> c2 implies c1.name <> c2.name)
The exists Operation
context LoyaltyAccount
inv: points > 0 implies transactions-> exists(t ▏t.points > 0)
collection->exists(‹expression›)
not collection->forAll(not ‹expression›)
The one Operation
context LoyaltyProgram
inv: self.Membership.account-> one(number<10000)
The collect Operation
context LoyaltyAccount
inv: transactions->collect( points )-> exists( p : Integer ▏p = 500 )
context LoyaltyAccount
inv: transactions.points->exists(p : Integer ▏p = 500 )
The collectNested Operation
self.programs->collect(partners)-> collectNested( deliveredServices )
the type of this expression is Bag(Set(Service))
The iterate Operation
collection->iterate( element : Type1; result:Type2 = ‹expression› ▏‹expression-with-element-and-result›)
Set{1,2,3}->iterate( i: Integer, sum: Integer = 0 ▏sum + i )
context ProgramPartner
def:getBurningTransactions():Set(Transaction) =
self.deliveredServices.transactions->iterate(
t: Transaction;
resultSet:Set(Transaction) = Set{}
if t.oclIsTypeOf(Burning) then
resultSet.including(t)
else
resultSet
endif
)