Skip to content

Commit 03c517e

Browse files
authored
Generic map and reduce (#86)
* Generic Map/Reduce functions * fix httputilx test
1 parent 3d567cb commit 03c517e

File tree

3 files changed

+81
-6
lines changed

3 files changed

+81
-6
lines changed

httputilx/httputilx_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ func TestFetch(t *testing.T) {
202202
cases := []struct {
203203
in, want, wantErr string
204204
}{
205-
{"http://example.com", "<html>", ""},
205+
{"http://example.com", "<html", ""},
206206
{"http://fairly-certain-this-doesnt-exist-asdasd12g1ghdfddd.com", "", "cannot download"},
207207
{"http://httpbin.org/status/400", "", "400"},
208208
{"http://httpbin.org/status/500", "", "500"},

sliceutil/sliceutil.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,18 +162,27 @@ func FilterEmpty[T comparable](t T) bool {
162162
}
163163

164164
// Map returns a list where each item in list has been modified by fn
165-
func Map[T comparable](tt []T, fn func(T) T) []T {
166-
ret := make([]T, len(tt))
165+
func Map[InputTyp, OutputTyp any](tt []InputTyp, fn func(InputTyp) OutputTyp) []OutputTyp {
166+
ret := make([]OutputTyp, len(tt))
167167
for i, t := range tt {
168168
ret[i] = fn(t)
169169
}
170170

171171
return ret
172172
}
173173

174-
// InterfaceSliceTo converts []interface to any given slice.
175-
// It will ~optimistically~ try to convert interface item to the dst item type
176-
func InterfaceSliceTo(src []interface{}, dst interface{}) interface{} {
174+
// Reduce returns an aggregation of tt
175+
func Reduce[InputTyp, AccTyp any](tt []InputTyp, fn func(AccTyp, InputTyp) AccTyp, acc AccTyp) AccTyp {
176+
for _, t := range tt {
177+
acc = fn(acc, t)
178+
}
179+
180+
return acc
181+
}
182+
183+
// InterfaceSliceTo converts []any to any given slice.
184+
// It will ~optimistically~ try to convert any item to the dst item type
185+
func InterfaceSliceTo(src []any, dst any) any {
177186
dstt := reflect.TypeOf(dst)
178187
if dstt.Kind() != reflect.Slice {
179188
panic("`dst` is not an slice")

sliceutil/sliceutil_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,72 @@ func TestMap_String(t *testing.T) {
576576
}
577577
}
578578

579+
func TestMap_Int(t *testing.T) {
580+
cases := []struct {
581+
in []string
582+
want []int
583+
f func(string) int
584+
}{
585+
{
586+
in: []string{"1", "5", "2"},
587+
want: []int{1, 5, 2},
588+
f: func(s string) int {
589+
n, _ := strconv.Atoi(s) // notlint: errcheck
590+
return n
591+
},
592+
},
593+
}
594+
for _, tc := range cases {
595+
t.Run("", func(t *testing.T) {
596+
out := Map(tc.in, tc.f)
597+
if !reflect.DeepEqual(tc.want, out) {
598+
t.Errorf("\nout: %#v\nwant: %#v\n", out, tc.want)
599+
}
600+
})
601+
}
602+
}
603+
604+
func TestReduce_Int(t *testing.T) {
605+
cases := []struct {
606+
in []int
607+
want int64
608+
f func(int64, int) int64
609+
}{
610+
{
611+
in: []int{1, 5, 2},
612+
want: 8,
613+
f: func(acc int64, i int) int64 {
614+
return acc + int64(i)
615+
},
616+
},
617+
}
618+
for _, tc := range cases {
619+
t.Run("", func(t *testing.T) {
620+
out := Reduce(tc.in, tc.f, 0)
621+
if !reflect.DeepEqual(tc.want, out) {
622+
t.Errorf("\nout: %#v\nwant: %#v\n", out, tc.want)
623+
}
624+
})
625+
}
626+
}
627+
628+
func TestReduce_Struct(t *testing.T) {
629+
type Acc struct {
630+
acc int
631+
}
632+
633+
out := Reduce(
634+
[]int{1, 2, 3},
635+
func(out Acc, i int) Acc {
636+
out.acc += i
637+
return out
638+
},
639+
Acc{})
640+
if out.acc != 6 {
641+
t.Errorf("\nout: %#v\nwant: %#v\n", out.acc, 6)
642+
}
643+
}
644+
579645
func TestInterfaceSliceTo(t *testing.T) {
580646
{
581647
src := []interface{}{"1", "2", "3", "4", "5"}

0 commit comments

Comments
 (0)