diff --git a/crates/typst/src/foundations/array.rs b/crates/typst/src/foundations/array.rs index 7d76cf5bb..6bde7d6c7 100644 --- a/crates/typst/src/foundations/array.rs +++ b/crates/typst/src/foundations/array.rs @@ -882,6 +882,36 @@ impl Array { }) .collect() } + + /// Reduces the elements to a single one, by repeatedly applying a reducing + /// operation. + /// + /// If the array is empty, returns `none`, otherwise, returns the + /// result of the reduction. + /// + /// The reducing function is a closure with two arguments: an 'accumulator', and an element. + /// + /// For arrays with at least one element, this is the same as `array.fold` + /// with the first element of the array as the initial accumulator value, folding + /// every subsequent element into it. + #[func] + pub fn reduce( + self, + /// The engine. + engine: &mut Engine, + /// The callsite context. + context: Tracked, + /// The reducing function. Must have two parameters: One for the + /// accumulated value and one for an item. + reducer: Func, + ) -> SourceResult { + let mut iter = self.into_iter(); + let mut acc = iter.next().unwrap_or_default(); + for item in iter { + acc = reducer.call(engine, context, [acc, item])?; + } + Ok(acc) + } } /// A value that can be cast to bytes. diff --git a/tests/suite/foundations/array.typ b/tests/suite/foundations/array.typ index 3992d75ed..336c5a67b 100644 --- a/tests/suite/foundations/array.typ +++ b/tests/suite/foundations/array.typ @@ -492,3 +492,16 @@ --- array-unclosed --- // Error: 3-4 unclosed delimiter #{(} + +--- array-reduce --- +// Test the `reduce` method. +#test(().reduce(grid), none) +#test((1, 2, 3, 4).reduce((s, x) => s + x), 10) + +--- array-reduce-missing-reducer --- +// Error: 2-13 missing argument: reducer +#().reduce() + +--- array-reduce-unexpected-argument --- +// Error: 19-21 unexpected argument +#(1, 2, 3).reduce(() => none) \ No newline at end of file