DM14
DM14

Reputation: 307

How to SortBy two parameters in OCL?

I need to sort the collection of Persons by two parameters, by Surnames and after by Names. How can I do something like this in OCL?

Upvotes: 1

Views: 751

Answers (3)

Ed Willink
Ed Willink

Reputation: 1498

Thanks you inspired me. I just raised http://issues.omg.org/browse/OCL25-213 whose text is:

The sortedBy iteration provides an elegant solution to a sort problem in which the sort metric is a projection of the sorted object. Thus sortedBy(p|p.name) or just sortedBy(name) is short and avoids the opportunities for typos from a more conventional exposition involving comparison of two objects. The natural solution may well be an efficient one for large collections with non-trivial metrics.

However the sortedBy solution is unfamiliar and so confusing for newcomers and unsuitable for multi-key sorting for which an artificial compound single key may need to be constructed.

One solution might be to provide a more conventional iterator such as sort(p1, p2 | comparison-expression) allowing a two key sort:

sort(p1, p2 | let diff1 = p1.key1.compareTo(p2.key1) in if diff1 <> 0 the diff 1 else p1.key2.compareTo(p2.key2) endif)

However this has poor readability and ample opportunities for typos.

Alternatively sortedBy with a Tuple-valued metric might support multiple keys as:

sortedBy(Tuple{first=key1,second=key2})

(The alphabetical order of the Tuple part names determines the priority.)

(Since sortedBy is declaratively clear and compact, inefficient small/trivial implementations can be optimized to their sort() equivalents.)

Upvotes: 1

Ed Willink
Ed Willink

Reputation: 1498

The OCL sortedBy(lambda) appears to be very different to Java's sort(comparator) apparently requiring a projection of the objects as the metric for sorting. However if the projection is self, you have a different Java functionality. Therefore if you do sortedBy(p | p) the sorting is dependent on the < operation for p.

To facilitate this, the Eclipse OCL prototype of a future OCL introduces an OclComparable type with a compareTo method enabling all relational operations to be realized provided your custom type extends the OclComparable type.

(A similar OclSummable with zero() and sum() operates supports Collection::sum() generically; e.g. String realizes sum as a concatenation.)

Upvotes: 0

Vincent Aranega
Vincent Aranega

Reputation: 1536

The sortedBy function sorts elements using a the criteria expressed in its body and a < relationship between each gathered result.

In your case, assuming that you have a surname attribute, the following statement will sort the collection c using the < operator on each surname gathered (so a < on strings):

c->sortedBy(p | p.surname)

An idea could be to compute a unique string using the surname and the name concatenated toghether. Thus, if you have:

  • George Smith
  • Garry Smith
  • George Smath

The comparison would be done between "Smith_George", "Smith_Garry" and "Smath_George" and would be ordered, following the lexicographical order, to:

  1. George Smath (Smath_George)
  2. Garry Smith (Smith_Garry)
  3. George Smith (smith_George)

Finally, the OCL request would be (assuming surname and name as existing attributes):

c->sortedBy(p | p.surname + '_' + p.name)

This little trick does the job, but it is not "exactly" a two parameters comparison for sortedBy.

Upvotes: 1

Related Questions