You can use groupby in OCL to divide a collection into groups that share the same value, such as grouping all cars by brand for a summary or chart.
Syntax
collection->groupby(item | groupingExpression)collection is the collection you want to summarize. The expression after the vertical bar (|) is evaluated for every item and supplies the value used as the group key.
For example, this expression groups every Car by its BrandOfCar association:
Car.allInstances->groupby(c | c.BrandOfCar)Result
groupby returns a collection of tuples. A tuple is a value with named parts. Every returned tuple contains:
| Tuple part | Contains | Example when grouping cars by brand |
|---|---|---|
| The grouping criterion | The value returned by the grouping expression. Its part name is based on that expression. | BrandOfCar
|
List
|
The collection of source objects that match that grouping criterion. | All Car objects for that brand
|
When the grouping expression is c.BrandOfCar, you can access the brand with self.BrandOfCar and the cars in that group with self.List. For example, self.List->size returns the number of cars in the current brand group.
Group cars by brand
Use the following procedure in a ViewModel when you want one row per car brand and a count of cars for that brand.
- Add a column whose expression returns all cars:
Car.allInstances- Change that column expression to group the cars:
Car.allInstances->groupby(c | c.BrandOfCar)- If MDriven Designer reports that a referring column expects
Carbut receivesBrandOfCar.BrandOfCar+List Collection (Car), find the column typed asCar : Carand set its class to Derive type from referring column. The referring column now returns group tuples, not individualCarobjects. - Add or update a brand-name column to use the grouping criterion. If the current object is the group tuple, use:
self.BrandOfCar.Name- Add or update a count column to use the grouped collection:
self.List->size- Name the count column
NumberOfCarsOfBrand, save, and test the ViewModel.
For a group containing five cars of one brand, self.List->size returns 5.
Calculate each group's percentage
You can calculate the proportion that each group represents of all cars. The following expression returns a rounded percentage string for the current group:
(100*self.List->size/Car.allinstances->size).Round(0).asstring+'%'For example, if the model contains 20 cars and the current brand group contains 5 cars, the expression returns '25%'.
This expression uses the group’s List for the numerator and all Car instances for the denominator. Ensure that the source collection contains the objects you intend to include before using it for a percentage.
Create a summarized collection
Use collect after groupby when you need a smaller result for presentation, such as a brand name and its percentage. This example creates a tuple for each brand group:
Car.allInstances->groupby(c|c.BrandOfCar)->collect(tuple|
tuple.BrandOfCar.Name,
(100*tuple.List->size/Car.allinstances->size).Round(0).asstring+'%'
)The new tuples have positional parts. In a ViewModel based on this result:
- Use
self.Namefor the brand-name part. - Use
self.Part2for the percentage part.
This form is useful when preparing values for an HTML or SVG chart. See Training:Bootcamp:Chapter 13 for a walkthrough that groups cars by brand and uses the results in a chart.
When to use groupby
Use groupby when you need to work with the objects in each group as well as the grouping value. Typical uses include:
- Count objects per grouping value with
self.List->size. - Calculate a percentage from a group count and a total count.
- Produce one presentation row per group.
- Prepare grouped values for a chart.
The result is not a collection of the original objects. It is a collection of group tuples, so columns and expressions that previously expected an individual source object must use the tuple’s grouping part or its List part instead.
