Skip to content

Commit 94bde0f

Browse files
author
Dean Karn
authored
Add sliceext and mapext generic helper functions (#19)
1 parent 4560717 commit 94bde0f

File tree

8 files changed

+242
-2
lines changed

8 files changed

+242
-2
lines changed

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ test:
66
bench:
77
$(GOCMD) test -run=NONE -bench=. -benchmem ./...
88

9-
.PHONY: linters-install lint test bench
9+
lint:
10+
golangci-lint run
11+
12+
.PHONY: lint test bench

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pkg
22

3-
![Project status](https://img.shields.io/badge/version-5.10.0-green.svg)
3+
![Project status](https://img.shields.io/badge/version-5.11.0-green.svg)
44
[![Build Status](https://travis-ci.org/go-playground/pkg.svg?branch=master)](https://travis-ci.org/go-playground/pkg)
55
[![Coverage Status](https://coveralls.io/repos/github/go-playground/pkg/badge.svg?branch=master)](https://coveralls.io/github/go-playground/pkg?branch=master)
66
[![GoDoc](https://godoc.org/github.com/go-playground/pkg?status.svg)](https://pkg.go.dev/mod/github.com/go-playground/pkg/v5)

map/map.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//go:build go1.18
2+
// +build go1.18
3+
4+
package mapext
5+
6+
// Retain retains only the elements specified by the function and removes others.
7+
func Retain[K comparable, V any](m map[K]V, fn func(key K, value V) bool) {
8+
for k, v := range m {
9+
if fn(k, v) {
10+
continue
11+
}
12+
delete(m, k)
13+
}
14+
}
15+
16+
// Map allows mapping of a map[K]V -> U.
17+
func Map[K comparable, V any, U any](m map[K]V, init U, fn func(accum U, key K, value V) U) U {
18+
accum := init
19+
for k, v := range m {
20+
accum = fn(accum, k, v)
21+
}
22+
return accum
23+
}

map/map_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//go:build go1.18
2+
// +build go1.18
3+
4+
package mapext
5+
6+
import (
7+
. "github.com/go-playground/assert/v2"
8+
"sort"
9+
"testing"
10+
)
11+
12+
func TestRetain(t *testing.T) {
13+
m := map[string]int{
14+
"0": 0,
15+
"1": 1,
16+
"2": 2,
17+
"3": 3,
18+
}
19+
Retain(m, func(key string, value int) bool {
20+
return value < 1 || value > 2
21+
})
22+
Equal(t, len(m), 2)
23+
Equal(t, m["0"], 0)
24+
Equal(t, m["3"], 3)
25+
}
26+
27+
func TestMap(t *testing.T) {
28+
// Test Map to slice
29+
m := map[string]int{
30+
"0": 0,
31+
"1": 1,
32+
}
33+
slice := Map(m, make([]int, 0, len(m)), func(accum []int, key string, value int) []int {
34+
return append(accum, value)
35+
})
36+
sort.SliceStable(slice, func(i, j int) bool {
37+
return i < j
38+
})
39+
Equal(t, len(slice), 2)
40+
41+
// Test Map to Map of different type
42+
inverted := Map(m, make(map[int]string, len(m)), func(accum map[int]string, key string, value int) map[int]string {
43+
accum[value] = key
44+
return accum
45+
})
46+
Equal(t, len(inverted), 2)
47+
Equal(t, inverted[0], "0")
48+
Equal(t, inverted[1], "1")
49+
}

slice/slice.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//go:build go1.18
2+
// +build go1.18
3+
4+
package sliceext
5+
6+
import (
7+
optionext "github.com/go-playground/pkg/v5/values/option"
8+
"sort"
9+
)
10+
11+
// Retain retains only the elements specified by the function.
12+
//
13+
// This shuffles and returns the retained values of the slice.
14+
func Retain[T any](slice []T, fn func(v T) bool) []T {
15+
var j int
16+
for _, v := range slice {
17+
if fn(v) {
18+
slice[j] = v
19+
j++
20+
}
21+
}
22+
return slice[:j]
23+
}
24+
25+
// Filter filters out the elements specified by the function.
26+
//
27+
// This shuffles and returns the retained values of the slice.
28+
func Filter[T any](slice []T, fn func(v T) bool) []T {
29+
var j int
30+
for _, v := range slice {
31+
if fn(v) {
32+
continue
33+
}
34+
slice[j] = v
35+
j++
36+
}
37+
return slice[:j]
38+
}
39+
40+
// Map maps a slice of []T -> []U using the map function.
41+
func Map[T, U any](slice []T, init U, fn func(accum U, v T) U) U {
42+
if len(slice) == 0 {
43+
return init
44+
}
45+
accum := init
46+
for _, v := range slice {
47+
accum = fn(accum, v)
48+
}
49+
return accum
50+
}
51+
52+
// Sort sorts the sliceWrapper x given the provided less function.
53+
//
54+
// The sort is not guaranteed to be stable: equal elements
55+
// may be reversed from their original order.
56+
//
57+
// For a stable sort, use SortStable.
58+
func Sort[T any](slice []T, less func(i T, j T) bool) {
59+
sort.Slice(slice, func(j, k int) bool {
60+
return less(slice[j], slice[k])
61+
})
62+
}
63+
64+
// SortStable sorts the sliceWrapper x using the provided less
65+
// function, keeping equal elements in their original order.
66+
func SortStable[T any](slice []T, less func(i T, j T) bool) {
67+
sort.SliceStable(slice, func(j, k int) bool {
68+
return less(slice[j], slice[k])
69+
})
70+
}
71+
72+
// Reduce reduces the elements to a single one, by repeatedly applying a reducing function.
73+
func Reduce[T any](slice []T, fn func(accum T, current T) T) optionext.Option[T] {
74+
if len(slice) == 0 {
75+
return optionext.None[T]()
76+
}
77+
accum := slice[0]
78+
for _, v := range slice {
79+
accum = fn(accum, v)
80+
}
81+
return optionext.Some(accum)
82+
}

slice/slice_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//go:build go1.18
2+
// +build go1.18
3+
4+
package sliceext
5+
6+
import (
7+
. "github.com/go-playground/assert/v2"
8+
optionext "github.com/go-playground/pkg/v5/values/option"
9+
"strconv"
10+
"testing"
11+
)
12+
13+
func TestFilter(t *testing.T) {
14+
s := Filter([]int{0, 1, 2, 3}, func(v int) bool {
15+
return v > 0 && v < 3
16+
})
17+
Equal(t, len(s), 2)
18+
Equal(t, s[0], 0)
19+
Equal(t, s[1], 3)
20+
21+
}
22+
23+
func TestRetain(t *testing.T) {
24+
s := Retain([]int{0, 1, 2, 3}, func(v int) bool {
25+
return v > 0 && v < 3
26+
})
27+
Equal(t, len(s), 2)
28+
Equal(t, s[0], 1)
29+
Equal(t, s[1], 2)
30+
}
31+
32+
func TestMap(t *testing.T) {
33+
s := Map[int, []string]([]int{0, 1, 2, 3}, make([]string, 0, 4), func(accum []string, v int) []string {
34+
return append(accum, strconv.Itoa(v))
35+
})
36+
Equal(t, len(s), 4)
37+
Equal(t, s[0], "0")
38+
Equal(t, s[1], "1")
39+
Equal(t, s[2], "2")
40+
Equal(t, s[3], "3")
41+
42+
// Test Map empty slice
43+
s2 := Map[int, []string](nil, nil, func(accum []string, v int) []string {
44+
return append(accum, strconv.Itoa(v))
45+
})
46+
Equal(t, len(s2), 0)
47+
}
48+
49+
func TestSort(t *testing.T) {
50+
s := []int{0, 1, 2}
51+
Sort(s, func(i int, j int) bool {
52+
return i > j
53+
})
54+
Equal(t, s[0], 2)
55+
Equal(t, s[1], 1)
56+
Equal(t, s[2], 0)
57+
}
58+
59+
func TestSortStable(t *testing.T) {
60+
s := []int{0, 1, 1, 2}
61+
SortStable(s, func(i int, j int) bool {
62+
return i > j
63+
})
64+
Equal(t, s[0], 2)
65+
Equal(t, s[1], 1)
66+
Equal(t, s[2], 1)
67+
Equal(t, s[3], 0)
68+
}
69+
70+
func TestReduce(t *testing.T) {
71+
result := Reduce([]int{0, 1, 2}, func(accum int, current int) int {
72+
return accum + current
73+
})
74+
Equal(t, result, optionext.Some(3))
75+
76+
// Test Reduce empty slice
77+
result = Reduce([]int{}, func(accum int, current int) int {
78+
return accum + current
79+
})
80+
Equal(t, result, optionext.None[int]())
81+
}

sync/mutex.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//go:build go1.18
2+
// +build go1.18
23

34
package syncext
45

sync/mutex_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//go:build go1.18
2+
// +build go1.18
23

34
package syncext
45

0 commit comments

Comments
 (0)