How do I use collect as to aggregate stream results in EK9?
← Collections and Data Structures · Ref: Q122
EK9 stream pipelines use | collect as Type to gather results. Built-in collectors handle List, Integer, and String. For custom aggregation, define operator | on a record.
BUILT-IN COLLECTORS
Collect into a list:
evens <- cat numbers | filter by isEven | collect as List of Integer
Collect by summing integers:
total <- cat numbers | collect as Integer
Collect by concatenating strings:
joined <- cat words | collect as String
CUSTOM AGGREGATOR PATTERN
Define a record with operator | to receive stream items one at a time:
Stats
count as Integer: Integer()
total as Integer: Integer()
operator |
-> arg as Integer
if arg?
if ~total?
count := 1
total :=: arg
else
count++
total += arg
The stream feeds each item to operator | in sequence.
TRI-STATE FIRST-ITEM DETECTION
Use ~total? to detect the first item (total is unset). On first item, initialize with :=: (copy operator). On subsequent items, accumulate with +=.
USAGE
stats <- for i in 1 ... 12 | collect as Stats
The for-range generates integers 1 through 12, piped into the Stats aggregator.
See Q80 for for-range expression as alternative accumulation. See Q82 for for-in expression as alternative fold/reduce. See Q89 for basic collect patterns. See Q96 for operator overloading. See Q120 for sort before collect. See Q124 for group by aggregation. See Q235 for complete stream operations reference. See Q237 for streams vs loops decision guide. See Q286 for batch accumulation with stream collect.
Example
defines module qa.collections.collectas defines record Stats count as Integer: Integer() total as Integer: Integer() average as Float: Float() operator $ as pure <- rtn as String: String() if total? rtn :=? `[${count}, ${average}, ${total}]` operator | -> arg as Integer if arg? if ~total? count := 1 total :=: arg else count++ total += arg average := (#^total) / count default operator ? defines function isEven() as pure -> num as Integer <- rtn as Boolean: num mod 2 == 0 doubleIt() as pure -> num as Integer <- rtn as Integer: num * 2 defines program CollectAsDemo() stdout <- Stdout() numbers <- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // === BUILT-IN: COLLECT AS LIST === evens <- cat numbers | filter by isEven | collect as List of Integer stdout.println(`Evens: ${evens}`) // === BUILT-IN: COLLECT AS INTEGER (SUM) === total <- cat numbers | collect as Integer stdout.println(`Sum: ${total}`) // === CUSTOM AGGREGATOR === stats <- for i in 1 ... 12 | collect as Stats stdout.println(`Stats: ${stats}`) // === CUSTOM AGGREGATOR WITH FILTER === evenStats <- cat numbers | filter by isEven | collect as Stats stdout.println(`Even stats: ${evenStats}`)
Common mistakes
E50060 — EK9 does not have .stream() or .collect() methods. Use the cat source | filter by pred | collect as Type pipeline syntax. See ek9 -h E50060 for details.
Incorrect:
evens <- numbers.stream().filter(isEven).collect()
Correct:
evens <- cat numbers | filter by isEven | collect as List of Integer
E07235 — A record with fields must define operator ? for tri-state semantics. A Stats record used with '| collect as' needs isSet so the pipeline can check if the result is valid. See ek9 -h E07235 for details.
Incorrect:
//no isSet operator
Correct:
default operator ?
E50060 — EK9 does not have toString(). Use string interpolation or the $ operator. See ek9 -h E50060 for details.
Incorrect:
stdout.println(evens.toString())
Correct:
stdout.println(`Evens: ${evens}`)
E50060 — EK9 does not have toString(). Use string interpolation or the $ operator for string conversion. See ek9 -h E50060 for details.
Incorrect:
stdout.println(total.toString())
Correct:
stdout.println(`Sum: ${total}`)
E07520 — A function used with '| filter by' must return Boolean. The function doubleIt() returns Integer, not Boolean, so it cannot be used as a predicate. Use a function that returns Boolean for filtering. See ek9 -h E07520 for details.
Incorrect:
cat numbers | filter by doubleIt | collect as List of Integer
Correct:
cat numbers | filter by isEven | collect as List of Integer
Other ways to ask this
- How do I create a custom stream collector in EK9?
- How does the operator | work for stream aggregation in EK9?
- How do I reduce a stream to a single value in EK9?
- What is the EK9 equivalent of fold or reduce?
- How do I accumulate values from a stream in EK9?
Coming from another language?
Java: Collectors.reducing(), Collectors.summarizingInt(), custom Collector interface. Python: functools.reduce() or manual accumulation. JavaScript: array.reduce((acc, item) => ..., initial). Rust: iter.fold(initial, |acc, item| ...). Go: manual loop accumulation. EK9: define operator | on a record, use | collect as MyRecord in pipeline, tri-state ~var? for first-item detection.
Keywords: operator, reduce, accumulator, collect, sum, reducer, pipe, data-structure, custom, result, collection, aggregate, stream, fold, accumulate, statistics