Skip to content

Commit cbac55e

Browse files
drewolsonlpil
authored andcommitted
Add flat_map and friends to iterator
The following functions are added to iterator: * append * flatten * flat_map
1 parent aa1450b commit cbac55e

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- The `dynamic` module gains the `option` function.
1111
- The `uri` module gains the `percent_encode` and `percent_decode` functions.
1212
- The `os` module gains the `erlang_timestamp` function.
13+
- The `iterator` module gains the `append`, `flatten` and `flat_map` functions.
1314

1415
## v0.11.0 - 2020-08-22
1516

src/gleam/iterator.gleam

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,83 @@ pub fn map(over iterator: Iterator(a), with f: fn(a) -> b) -> Iterator(b) {
268268
|> Iterator
269269
}
270270

271+
fn do_append(
272+
first: fn() -> Action(a),
273+
second: fn() -> Action(a),
274+
) -> fn() -> Action(a) {
275+
fn() {
276+
case first() {
277+
Continue(e, first) -> Continue(e, do_append(first, second))
278+
Stop -> second()
279+
}
280+
}
281+
}
282+
283+
/// Append two iterators, producing a new iterator.
284+
///
285+
/// This function does not evaluate the elements of the iterators, the
286+
/// computation is performed when the resulting iterator is later run.
287+
///
288+
/// ## Examples
289+
///
290+
/// > [1, 2] |> from_list |> append([3, 4] |> from_list) |> to_list
291+
/// [1, 2, 3, 4]
292+
///
293+
pub fn append(to first: Iterator(a), suffix second: Iterator(a)) -> Iterator(a) {
294+
first.continuation
295+
|> do_append(second.continuation)
296+
|> Iterator
297+
}
298+
299+
fn do_flatten(continuation: fn() -> Action(Iterator(a))) -> fn() -> Action(a) {
300+
fn() {
301+
case continuation() {
302+
Continue(e, continuation) ->
303+
do_append(e.continuation, do_flatten(continuation))()
304+
Stop -> Stop
305+
}
306+
}
307+
}
308+
309+
/// Flatten an iterator of iterator of iterators, creating a new iterator.
310+
///
311+
/// This function does not evaluate the elements of the iterator, the
312+
/// computation is performed when the iterator is later run.
313+
///
314+
/// ## Examples
315+
///
316+
/// > [[1, 2], [3, 4]] |> list.map(from_list) |> flatten |> to_list
317+
/// [1, 2, 3, 4]
318+
///
319+
pub fn flatten(iterator: Iterator(Iterator(a))) -> Iterator(a) {
320+
iterator.continuation
321+
|> do_flatten
322+
|> Iterator
323+
}
324+
325+
/// Create an iterator from an existing iterator and a transformation function.
326+
///
327+
/// Each element in the new iterator will be the result of calling the given
328+
/// function on the elements in the given iterator and then flattening the
329+
/// results.
330+
///
331+
/// This function does not evaluate the elements of the iterator, the
332+
/// computation is performed when the iterator is later run.
333+
///
334+
/// ## Examples
335+
///
336+
/// > [1, 2] |> from_list |> flat_map(fn(x) { from_list([x, x + 1]) }) |> to_list
337+
/// [1, 2, 2, 3]
338+
///
339+
pub fn flat_map(
340+
over iterator: Iterator(a),
341+
with f: fn(a) -> Iterator(b),
342+
) -> Iterator(b) {
343+
iterator
344+
|> map(f)
345+
|> flatten
346+
}
347+
271348
fn do_filter(
272349
continuation: fn() -> Action(e),
273350
predicate: fn(e) -> Bool,

test/gleam/iterator_test.gleam

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,60 @@ pub fn map_test() {
7171
test([1, 2, 3, 4, 5, 6, 7, 8], f)
7272
}
7373

74+
// a |> from_list |> flat_map(f) |> to_list ==
75+
// a |> list.map(f) |> list.map(to_list) |> list.flatten
76+
pub fn flat_map_test() {
77+
let test = fn(subject, f) {
78+
subject
79+
|> iterator.from_list
80+
|> iterator.flat_map(f)
81+
|> iterator.to_list
82+
|> should.equal(
83+
subject
84+
|> list.map(f)
85+
|> list.map(iterator.to_list)
86+
|> list.flatten,
87+
)
88+
}
89+
90+
let f = fn(i) { iterator.range(i, i + 2) }
91+
92+
test([], f)
93+
test([1], f)
94+
test([1, 2], f)
95+
}
96+
97+
// a |> from_list |> append(from_list(b)) |> to_list == list.flatten([a, b])
98+
pub fn append_test() {
99+
let test = fn(left, right) {
100+
left
101+
|> iterator.from_list
102+
|> iterator.append(iterator.from_list(right))
103+
|> iterator.to_list
104+
|> should.equal(list.flatten([left, right]))
105+
}
106+
107+
test([], [])
108+
test([1], [2])
109+
test([1, 2], [3, 4])
110+
}
111+
112+
// a |> list.map(from_list) |> flatten |> to_list == list.flatten(a)
113+
pub fn flatten_test() {
114+
let test = fn(lists) {
115+
lists
116+
|> list.map(iterator.from_list)
117+
|> iterator.from_list
118+
|> iterator.flatten
119+
|> iterator.to_list
120+
|> should.equal(list.flatten(lists))
121+
}
122+
123+
test([[], []])
124+
test([[1], [2]])
125+
test([[1, 2], [3, 4]])
126+
}
127+
74128
// a |> from_list |> filter(f) |> to_list == a |> list.filter(_, f)
75129
pub fn filter_test() {
76130
let test = fn(subject, f) {

0 commit comments

Comments
 (0)