How do I sort a collection in EK9?
← Collections and Data Structures · Ref: Q120
EK9 sorts collections through stream pipelines using the | sort operation. Natural sorting uses the <=> operator defined on the element type. Custom sorting uses a comparator function.
SORT IN STREAMS
Sorting is a pipeline stage, not a method call on the collection:
sorted <- cat items | sort | collect as List of String
This creates a new sorted list. The original list is unchanged.
NATURAL SORT
| sort uses the <=> (comparison) operator defined on the element type:
cat names | sort > stdout
For built-in types (String, Integer, Float, Date), <=> is already defined. For custom types, you must implement operator <=>.
SORT BY COMPARATOR
| sort by function uses a custom comparator:
cat books | sort by comparingTitle | collect as List of Book
The comparator is a pure function taking two arguments and returning Integer (-1, 0, or 1).
IMPLEMENTING COMPARISON FOR CUSTOM TYPES
Define operator <=> on your record or class to enable natural sort:
operator <=> as pure -> other as Product <- rtn as Integer: name <=> other.name
COMPARATOR AS DYNAMIC FUNCTION
Create comparators inline for one-off sorting:
comparingPrice <- () extends Comparator of Product as pure function r:=? t1.price() <=> t2.price()
This creates a function that compares by price field.
See Q45 for List basics. See Q89 for basic stream pipelines. See Q96 for operator overloading. See Q121 for PriorityQueue (maintained order). See Q125 for head/tail/skip to limit sorted results.
Example
defines module qa.collections.sort defines class Product name as String? price as Float? default private Product() as pure Product() as pure -> name as String price as Float this.name :=? name this.price :=? price name() as pure <- rtn as String: String(name) price() as pure <- rtn as Float: Float(price) operator <=> as pure -> other as Product <- rtn as Integer: name <=> other.name operator == as pure -> other as Product <- rtn as Boolean: name == other.name and price == other.price operator $ as pure <- rtn as String: `${name} \$${price}` operator #? as pure <- rtn as Integer: #?name override operator ? as pure <- rtn as Boolean: name? and price? defines function comparingPrice() as pure -> t1 as Product t2 as Product <- rtn as Integer: t1.price() <=> t2.price() defines program SortCollectionDemo() stdout <- Stdout() products <- [ Product("Banana", 1.20), Product("Apple", 0.90), Product("Cherry", 3.50), Product("Date", 5.00) ] // === NATURAL SORT === // Sort by name (uses operator <=> defined on Product) byName <- cat products | sort | collect as List of Product stdout.println(`By name: ${byName}`) // === SORT BY COMPARATOR === // Sort by price using named comparator byPrice <- cat products | sort by comparingPrice | collect as List of Product stdout.println(`By price: ${byPrice}`) // === SORT WITH DYNAMIC COMPARATOR === // Inline comparator for one-off sort reverseByName <- () extends Comparator of Product as pure function r:=? t2.name() <=> t1.name() descending <- cat products | sort by reverseByName | collect as List of Product stdout.println(`Descending: ${descending}`)
Common mistakes
E50060 — EK9 sorts through stream pipelines using '| sort', not method calls like .sort(). Sorting is a pipeline stage, not a method on the collection. See ek9 -h E50060 for details.
Incorrect:
byName <- products.sort()
Correct:
byName <- cat products | sort | collect as List of Product
E07235 — A class with fields must define operator ? for tri-state semantics. Without it, guard expressions and safe access patterns cannot inspect field state. See ek9 -h E07235 for details.
Incorrect:
//no isSet operator
Correct:
override operator ? as pure <- rtn as Boolean: name? and price?
E50060 — EK9 does not have toString(). Use the $ operator or string interpolation. See ek9 -h E50060 for details.
Incorrect:
stdout.println(byName.toString())
Correct:
stdout.println(`By name: ${byName}`)
E50001 — EK9 does not have Java-style Collections utility classes or method references. 'Collections' is not a known type. Use a named comparator function that takes two parameters and returns Integer. See ek9 -h E50001 for details.
Incorrect:
cat products | sort by Collections.comparing(Product.price) | collect as List of Product
Correct:
cat products | sort by comparingPrice | collect as List of Product
Other ways to ask this
- How do I order a list in EK9?
- How does the sort pipeline operation work in EK9?
- How do I sort by a custom comparator in EK9?
Coming from another language?
Java: Collections.sort() or list.stream().sorted(Comparator.comparing()). Python: sorted(list, key=lambda x: x.field). JavaScript: array.sort((a, b) => a.field - b.field). Rust: vec.sort() or vec.sort_by_key(). Go: sort.Slice() with less function. EK9: cat list | sort or cat list | sort by comparator in a pipeline, comparator defined as pure function or dynamic function extending Comparator of T.
Keywords: order, natural, ascending, comparison, data-structure, pipeline, sort, descending, collection, comparator, stream, list, custom