aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMarcin Puc <marcin.e.puc@gmail.com>2021-03-12 17:46:58 +0100
committerLouis Pilfold <louis@lpil.uk>2021-03-20 13:35:54 +0000
commit5072b0e961add30e0a03df5e9b8d1d6f455f7085 (patch)
tree7003d5644bfdabc609137b84172fc50308724338 /src
parent5f663745bbf4d74dabbcdb91f5398a29890af2cf (diff)
downloadgleam_stdlib-5072b0e961add30e0a03df5e9b8d1d6f455f7085.tar.gz
gleam_stdlib-5072b0e961add30e0a03df5e9b8d1d6f455f7085.zip
Add iterator.chunk_by
Diffstat (limited to 'src')
-rw-r--r--src/gleam/iterator.gleam68
1 files changed, 68 insertions, 0 deletions
diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam
index 6e0bf31..3fa74d0 100644
--- a/src/gleam/iterator.gleam
+++ b/src/gleam/iterator.gleam
@@ -6,6 +6,11 @@ type Action(element) {
Continue(element, fn() -> Action(element))
}
+// Shortcut for an empty iterator
+fn stop() -> Action(element) {
+ Stop
+}
+
/// An iterator is a lazily evaluated sequence of element.
///
/// Iterators are useful when working with collections that are too large to
@@ -655,3 +660,66 @@ pub fn zip(left: Iterator(a), right: Iterator(b)) -> Iterator(tuple(a, b)) {
do_zip(left.continuation, right.continuation)
|> Iterator
}
+
+type ChunkBy(element, key) {
+ AnotherBy(List(element), key, element, fn() -> Action(element))
+ LastBy(List(element))
+ NoneBy
+}
+
+fn next_chunk_by(
+ continuation: fn() -> Action(element),
+ f: fn(element) -> key,
+ previous_key: key,
+ current_chunk: List(element),
+) -> ChunkBy(element, key) {
+ case continuation() {
+ Stop ->
+ case current_chunk {
+ [] -> NoneBy
+ remaining -> LastBy(list.reverse(remaining))
+ }
+ Continue(e, next) -> {
+ let key = f(e)
+ case key == previous_key {
+ True -> next_chunk_by(next, f, key, [e, ..current_chunk])
+ False -> AnotherBy(list.reverse(current_chunk), key, e, next)
+ }
+ }
+ }
+}
+
+fn do_chunk_by(
+ continuation: fn() -> Action(element),
+ f: fn(element) -> key,
+ previous_key: key,
+ previous_element: element,
+) -> Action(List(element)) {
+ case next_chunk_by(continuation, f, previous_key, [previous_element]) {
+ NoneBy -> Stop
+ LastBy(chunk) -> Continue(chunk, stop)
+ AnotherBy(chunk, key, el, next) ->
+ Continue(chunk, fn() { do_chunk_by(next, f, key, el) })
+ }
+}
+
+/// Creates an iterator that emits chunks of elements
+/// for which `f` returns the same value.
+///
+/// ## Examples
+///
+/// > from_list([1, 2, 2, 3, 4, 4, 6, 7, 7]) |> chunk_by(fn(n) { n % 2 }) |> to_list
+/// [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
+///
+pub fn chunk_by(
+ over iterator: Iterator(element),
+ with f: fn(element) -> key,
+) -> Iterator(List(element)) {
+ fn() {
+ case iterator.continuation() {
+ Stop -> Stop
+ Continue(e, next) -> do_chunk_by(next, f, f(e), e)
+ }
+ }
+ |> Iterator
+}