Skip to content

Commit

Permalink
Slicesandmaps (#206)
Browse files Browse the repository at this point in the history
* Add maputil package for the non-thread-safe version of the tsmap package functions.

* Add sliceutil package for the non-thread-safe version of the tsslice package functions.

* Wrap the functions in the new sliceutil package.

* Wrap functions in the new maputil package and add Invert function.

* Bump version.
  • Loading branch information
nicolaasuni-vonage committed Aug 31, 2023
1 parent 6c597c5 commit b591960
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 47 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.78.4
1.79.0
2 changes: 1 addition & 1 deletion examples/service/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
replace github.com/Vonage/gosrvlib => ../..

require (
github.com/Vonage/gosrvlib v1.78.4
github.com/Vonage/gosrvlib v1.79.0
github.com/golang/mock v1.6.0
github.com/jstemmer/go-junit-report v0.9.1
github.com/prometheus/client_golang v1.16.0
Expand Down
57 changes: 57 additions & 0 deletions pkg/maputil/example_maputil_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package maputil_test

import (
"fmt"

"github.com/Vonage/gosrvlib/pkg/maputil"
)

func ExampleFilter() {
m := map[int]string{0: "Hello", 1: "World"}

filterFn := func(_ int, v string) bool { return v == "World" }

s2 := maputil.Filter(m, filterFn)

fmt.Println(s2)

// Output:
// map[1:World]
}

func ExampleMap() {
m := map[int]string{0: "Hello", 1: "World"}

mapFn := func(k int, v string) (string, int) { return "_" + v, k + 1 }

s2 := maputil.Map(m, mapFn)

fmt.Println(s2)

// Output:
// map[_Hello:1 _World:2]
}

func ExampleReduce() {
m := map[int]int{0: 2, 1: 3, 2: 5, 3: 7, 4: 11}
init := 97
reduceFn := func(k, v, r int) int { return k + v + r }

r := maputil.Reduce(m, init, reduceFn)

fmt.Println(r)

// Output:
// 135
}

func ExampleInvert() {
m := map[int]int{1: 10, 2: 20}

s2 := maputil.Invert(m)

fmt.Println(s2)

// Output:
// map[10:1 20:2]
}
53 changes: 53 additions & 0 deletions pkg/maputil/maputil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Package maputil provides a collection of map functions.
package maputil

// Filter returns a new map containing
// only the elements in the input map m for which the specified function f is true.
func Filter[M ~map[K]V, K comparable, V any](m M, f func(K, V) bool) M {
r := make(M, len(m))

for k, v := range m {
if f(k, v) {
r[k] = v
}
}

return r
}

// Map returns a new map that contains
// each of the elements of the input map m mutated by the specified function.
func Map[M ~map[K]V, K, J comparable, V, U any](m M, f func(K, V) (J, U)) map[J]U {
r := make(map[J]U, len(m))

for k, v := range m {
j, u := f(k, v)
r[j] = u
}

return r
}

// Reduce applies the reducing function f
// to each element of the input map m, and returns the value of the last call to f.
// The first parameter of the reducing function f is initialized with init.
func Reduce[M ~map[K]V, K comparable, V, U any](m M, init U, f func(K, V, U) U) U {
r := init

for k, v := range m {
r = f(k, v, r)
}

return r
}

// Invert returns a new map were keys and values are swapped.
func Invert[M ~map[K]V, K, V comparable](m M) map[V]K {
r := make(map[V]K, len(m))

for k, v := range m {
r[v] = k
}

return r
}
56 changes: 56 additions & 0 deletions pkg/maputil/maputil_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package maputil

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestFilter(t *testing.T) {
t.Parallel()

m := map[int]string{0: "Hello", 1: "World"}
filterFn := func(_ int, v string) bool { return v == "World" }

got := Filter(m, filterFn)

require.Len(t, got, 1)
require.Equal(t, "World", m[1])
}

func TestMap(t *testing.T) {
t.Parallel()

m := map[int]string{0: "Hello", 1: "World"}
mapFn := func(k int, v string) (string, int) { return "_" + v, k + 1 }

got := Map(m, mapFn)

require.Len(t, got, 2)
require.Equal(t, 1, got["_Hello"])
require.Equal(t, 2, got["_World"])
}

func TestReduce(t *testing.T) {
t.Parallel()

m := map[int]int{0: 2, 1: 3, 2: 5, 3: 7, 4: 11}
init := 97
reduceFn := func(k, v, r int) int { return k + v + r }

got := Reduce(m, init, reduceFn)

require.Equal(t, 135, got)
}

func TestInvert(t *testing.T) {
t.Parallel()

m := map[int]int{1: 10, 2: 20}

got := Invert(m)

require.Len(t, got, 2)
require.Equal(t, 1, got[10])
require.Equal(t, 2, got[20])
}
47 changes: 47 additions & 0 deletions pkg/sliceutil/example_sliceutil_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package sliceutil_test

import (
"fmt"

"github.com/Vonage/gosrvlib/pkg/sliceutil"
)

func ExampleFilter() {
s := []string{"Hello", "World", "Extra"}

filterFn := func(_ int, v string) bool { return v == "World" }

s2 := sliceutil.Filter(s, filterFn)

fmt.Println(s2)

// Output:
// [World]
}

func ExampleMap() {
s := []string{"Hello", "World", "Extra"}

mapFn := func(k int, v string) int { return k + len(v) }

s2 := sliceutil.Map(s, mapFn)

fmt.Println(s2)

// Output:
// [5 6 7]
}

func ExampleReduce() {
s := []int{2, 3, 5, 7, 11}

init := 97
reduceFn := func(k, v, r int) int { return k + v + r }

r := sliceutil.Reduce(s, init, reduceFn)

fmt.Println(r)

// Output:
// 135
}
41 changes: 41 additions & 0 deletions pkg/sliceutil/sliceutil.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Package sliceutil provides a collection of slice functions.
package sliceutil

// Filter returns a new slice containing
// only the elements in the input slice s for which the specified function f is true.
func Filter[S ~[]E, E any](s S, f func(int, E) bool) S {
r := make(S, 0)

for k, v := range s {
if f(k, v) {
r = append(r, v)
}
}

return r
}

// Map returns a new slice that contains
// each of the elements of the input slice s mutated by the specified function.
func Map[S ~[]E, E any, U any](s S, f func(int, E) U) []U {
r := make([]U, len(s))

for k, v := range s {
r[k] = f(k, v)
}

return r
}

// Reduce applies the reducing function f
// to each element of the input slice s, and returns the value of the last call to f.
// The first parameter of the reducing function f is initialized with init.
func Reduce[S ~[]E, E any, U any](s S, init U, f func(int, E, U) U) U {
r := init

for k, v := range s {
r = f(k, v, r)
}

return r
}
41 changes: 41 additions & 0 deletions pkg/sliceutil/sliceutil_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package sliceutil

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestFilter(t *testing.T) {
t.Parallel()

s := []string{"Hello", "World", "Extra"}
filterFn := func(_ int, v string) bool { return v == "World" }

got := Filter(s, filterFn)

require.ElementsMatch(t, []string{"World"}, got)
}

func TestMap(t *testing.T) {
t.Parallel()

s := []string{"Hello", "World", "Extra"}
mapFn := func(k int, v string) int { return k + len(v) }

got := Map(s, mapFn)

require.ElementsMatch(t, []int{5, 6, 7}, got)
}

func TestReduce(t *testing.T) {
t.Parallel()

s := []int{2, 3, 5, 7, 11}
init := 97
reduceFn := func(k, v, r int) int { return k + v + r }

got := Reduce(s, init, reduceFn)

require.Equal(t, 135, got)
}
13 changes: 13 additions & 0 deletions pkg/threadsafe/tsmap/example_tsmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,16 @@ func ExampleReduce() {
// Output:
// 135
}

func ExampleInvert() {
mux := &sync.RWMutex{}

m := map[int]int{1: 10, 2: 20}

s2 := tsmap.Invert(mux, m)

fmt.Println(s2)

// Output:
// map[10:1 20:2]
}
32 changes: 10 additions & 22 deletions pkg/threadsafe/tsmap/tsmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package tsmap

import (
"github.com/Vonage/gosrvlib/pkg/maputil"
"github.com/Vonage/gosrvlib/pkg/threadsafe"
)

Expand Down Expand Up @@ -35,15 +36,7 @@ func Filter[M ~map[K]V, K comparable, V any](mux threadsafe.RLocker, m M, f func
mux.RLock()
defer mux.RUnlock()

r := make(M, len(m))

for k, v := range m {
if f(k, v) {
r[k] = v
}
}

return r
return maputil.Filter(m, f)
}

// Map is a thread-safe function that returns a new map that contains
Expand All @@ -53,14 +46,7 @@ func Map[M ~map[K]V, K, J comparable, V, U any](mux threadsafe.RLocker, m M, f f
mux.RLock()
defer mux.RUnlock()

r := make(map[J]U, len(m))

for k, v := range m {
j, u := f(k, v)
r[j] = u
}

return r
return maputil.Map(m, f)
}

// Reduce is a thread-safe function that applies the reducing function f
Expand All @@ -70,11 +56,13 @@ func Reduce[M ~map[K]V, K comparable, V, U any](mux threadsafe.RLocker, m M, ini
mux.RLock()
defer mux.RUnlock()

r := init
return maputil.Reduce(m, init, f)
}

for k, v := range m {
r = f(k, v, r)
}
// Invert is a thread-safe function that returns a new map were keys and values are swapped.
func Invert[M ~map[K]V, K, V comparable](mux threadsafe.RLocker, m M) map[V]K {
mux.RLock()
defer mux.RUnlock()

return r
return maputil.Invert(m)
}
Loading

0 comments on commit b591960

Please sign in to comment.