Skip to content

Commit ba1fb1c

Browse files
committed
Add list functions with tests
1 parent e1c2db9 commit ba1fb1c

File tree

6 files changed

+219
-0
lines changed

6 files changed

+219
-0
lines changed

list/fold.go

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package list
22

3+
// FoldlIter is a left-associative fold of a chan.
34
func FoldlIter[T any, R any](init R, list <-chan T, fold func(carry R, next T) R) R {
45
for value := range list {
56
init = fold(init, value)
@@ -8,6 +9,7 @@ func FoldlIter[T any, R any](init R, list <-chan T, fold func(carry R, next T) R
89
return init
910
}
1011

12+
// Foldl is a left-associative fold of a list.
1113
func Foldl[T any, R any](init R, list []T, fold func(carry R, next T) R) R {
1214
for _, value := range list {
1315
init = fold(init, value)
@@ -16,6 +18,7 @@ func Foldl[T any, R any](init R, list []T, fold func(carry R, next T) R) R {
1618
return init
1719
}
1820

21+
// Foldr is a right-associative fold of a list.
1922
func Foldr[T any, R any](init R, list []T, fold func(carry R, next T) R) R {
2023
for idx := len(list) - 1; idx >= 0; idx-- {
2124
init = fold(init, list[idx])

list/map.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package list
2+
3+
// Map applies fn function to all elements of the list and returns with the list
4+
// of the results.
5+
func Map[T any, R any](list []T, fn func(T) R) []R {
6+
result := []R{}
7+
8+
for _, item := range list {
9+
result = append(result, fn(item))
10+
}
11+
12+
return result
13+
}
14+
15+
// MapIter applies fn function to all elements of a chan and returns with the
16+
// list of the results.
17+
func MapIter[T any, R any](list <-chan T, fn func(T) R) []R {
18+
result := []R{}
19+
20+
for item := range list {
21+
result = append(result, fn(item))
22+
}
23+
24+
return result
25+
}
26+
27+
// MapStream applies fn function to all elements of a list and streams back the
28+
// results in the result chan. Can be cancelled with the cancel function.
29+
func MapStream[T any, R any](list []T, fn func(T) R) (<-chan R, func()) {
30+
result := make(chan R)
31+
cancel := make(chan bool)
32+
cancelFn := func() { close(cancel) }
33+
34+
go func() {
35+
defer close(result)
36+
37+
for _, item := range list {
38+
select {
39+
case <-cancel:
40+
return
41+
default:
42+
}
43+
44+
result <- fn(item)
45+
}
46+
47+
}()
48+
49+
return result, cancelFn
50+
}
51+
52+
// MapIterStream applies fn function to all elements of a chan and streams back
53+
// the results in the result chan. Can be cancelled with the cancel function.
54+
func MapIterStream[T any, R any](list <-chan T, fn func(T) R) (<-chan R, func()) {
55+
result := make(chan R)
56+
cancel := make(chan bool)
57+
cancelFn := func() { close(cancel) }
58+
59+
go func() {
60+
defer close(result)
61+
62+
for item := range list {
63+
select {
64+
case <-cancel:
65+
return
66+
default:
67+
}
68+
69+
result <- fn(item)
70+
}
71+
72+
}()
73+
74+
return result, cancelFn
75+
}

list/map_test.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package list_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-asset/generics/list"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestMap(t *testing.T) {
11+
result := list.Map([]int{1, 2, 3}, func(v int) int { return v * 2 })
12+
13+
assert.Equal(t, []int{2, 4, 6}, result)
14+
}
15+
16+
func TestMapIter(t *testing.T) {
17+
ch := make(chan int)
18+
19+
go func() {
20+
for _, v := range []int{1, 2, 3} {
21+
ch <- v
22+
}
23+
24+
close(ch)
25+
}()
26+
27+
result := list.MapIter(ch, func(v int) int { return v * 2 })
28+
29+
assert.Equal(t, []int{2, 4, 6}, result)
30+
}
31+
32+
func TestMapStream(t *testing.T) {
33+
result, cancel := list.MapStream([]int{1, 2, 3}, func(v int) int {
34+
return v * 2
35+
})
36+
defer cancel()
37+
38+
endResult := []int{}
39+
40+
for v := range result {
41+
endResult = append(endResult, v)
42+
}
43+
44+
assert.Equal(t, []int{2, 4, 6}, endResult)
45+
}
46+
47+
func TestMapIterStream(t *testing.T) {
48+
ch := make(chan int)
49+
50+
go func() {
51+
for _, v := range []int{1, 2, 3} {
52+
ch <- v
53+
}
54+
55+
close(ch)
56+
}()
57+
58+
result, cancel := list.MapIterStream(ch, func(v int) int {
59+
return v * 2
60+
})
61+
defer cancel()
62+
63+
endResult := []int{}
64+
65+
for v := range result {
66+
endResult = append(endResult, v)
67+
}
68+
69+
assert.Equal(t, []int{2, 4, 6}, endResult)
70+
}

list/misc.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package list
2+
3+
import "github.com/go-asset/generics/maybe"
4+
5+
// Head extract the first element of a list as a Maybe, if the list is empty
6+
// it's Nothing.
7+
//
8+
// It's categorized as "misc" because it's very limited when it can be useful,
9+
// one example is chaining function calls with Maybe.
10+
//
11+
// doSomething(FromMaybe(Head(myList), 0))
12+
//
13+
// But most of the time, it feels useless. Why implementing it anyway? Because
14+
// why not. ;)
15+
func Head[T any](list []T) maybe.Maybe[T] {
16+
if len(list) == 0 {
17+
return maybe.Nothing[T]()
18+
}
19+
20+
return maybe.Just(list[0])
21+
}

list/misc_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package list_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-asset/generics/list"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestHead(t *testing.T) {
11+
result := list.Head([]int{})
12+
13+
assert.True(t, result.IsNothing())
14+
15+
result = list.Head([]int{1, 2, 3})
16+
17+
assert.False(t, result.IsNothing())
18+
assert.Equal(t, 1, result.Value())
19+
}

main.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"time"
6+
7+
"github.com/go-asset/generics/list"
8+
)
9+
10+
func main() {
11+
result, cancel := list.MapStream(
12+
[]int{1, 2, 3, 4},
13+
func(v int) int {
14+
time.Sleep(1 * time.Second)
15+
return v * 2
16+
},
17+
)
18+
defer cancel()
19+
20+
endResult := []int{}
21+
22+
for v := range result {
23+
log.Println(v)
24+
25+
if v == 4 {
26+
cancel()
27+
}
28+
29+
endResult = append(endResult, v)
30+
}
31+
}

0 commit comments

Comments
 (0)