diff options
-rw-r--r-- | src/gleam/iterator.gleam | 40 | ||||
-rw-r--r-- | test/gleam/iterator_test.gleam | 11 |
2 files changed, 51 insertions, 0 deletions
diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam index 4ea19f6..f9e39bb 100644 --- a/src/gleam/iterator.gleam +++ b/src/gleam/iterator.gleam @@ -1,4 +1,5 @@ import gleam/list +import gleam/map.{Map} // Internal private representation of an Iterator type Action(element) { @@ -879,3 +880,42 @@ pub fn all( iterator.continuation |> do_all(predicate) } + +fn update_group_with( + el: element, +) -> fn(Result(List(element), Nil)) -> List(element) { + fn(maybe_group) { + case maybe_group { + Ok(group) -> [el, ..group] + Error(Nil) -> [el] + } + } +} + +fn group_updater( + f: fn(element) -> key, +) -> fn(element, Map(key, List(element))) -> Map(key, List(element)) { + fn(elem, groups) { + groups + |> map.update(f(elem), update_group_with(elem)) + } +} + +/// Returns a `Map(k, List(element))` of elements from the given iterator +/// grouped with the given key function. +/// +/// The order within each group is preserved from the iterator. +/// +/// ## Examples +/// +/// > from_list([1, 2, 3, 4, 5, 6]) |> group(by: fn(n) { n % 3 }) +/// map.from_list([tuple(0, [3, 6]), tuple(1, [1, 4]), tuple(2, [2, 5])]) +/// +pub fn group( + in iterator: Iterator(element), + by key: fn(element) -> key, +) -> Map(key, List(element)) { + iterator + |> fold(map.new(), group_updater(key)) + |> map.map_values(fn(_, group) { list.reverse(group) }) +} diff --git a/test/gleam/iterator_test.gleam b/test/gleam/iterator_test.gleam index d08b63d..81a2908 100644 --- a/test/gleam/iterator_test.gleam +++ b/test/gleam/iterator_test.gleam @@ -1,6 +1,7 @@ import gleam/should import gleam/iterator.{Done, Next} import gleam/list +import gleam/map // a |> from_list |> to_list == a pub fn to_from_list_test() { @@ -362,3 +363,13 @@ pub fn all_test() { |> iterator.all(satisfying: fn(n) { n % 2 == 0 }) |> should.be_false } + +pub fn group_test() { + iterator.from_list([1, 2, 3, 4, 5, 6]) + |> iterator.group(by: fn(n) { n % 3 }) + |> should.equal(map.from_list([ + tuple(0, [3, 6]), + tuple(1, [1, 4]), + tuple(2, [2, 5]), + ])) +} |