Initial commit of modash
This was taken from github.com/lmika/gopkgs/fp
This commit is contained in:
commit
a20530ddfd
20 changed files with 425 additions and 0 deletions
18
moslice/filter.go
Normal file
18
moslice/filter.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package moslice
|
||||
|
||||
// Filter returns a slice containing all the elements of ts for which the passed in
|
||||
// predicate returns true. If no items match the predicate, the function will return
|
||||
// an empty slice. If ts is nil, the function will also return nil.
|
||||
func Filter[T any](ts []T, predicate func(t T) bool) []T {
|
||||
if ts == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
filteredTs := make([]T, 0)
|
||||
for _, t := range ts {
|
||||
if predicate(t) {
|
||||
filteredTs = append(filteredTs, t)
|
||||
}
|
||||
}
|
||||
return filteredTs
|
||||
}
|
||||
29
moslice/filter_test.go
Normal file
29
moslice/filter_test.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package moslice_test
|
||||
|
||||
import (
|
||||
"lmika.dev/pkg/modash/moslice"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
var (
|
||||
ints = []int{1, 2, 3, 4, 5}
|
||||
strs = []string{"foo", "bar", "baz"}
|
||||
)
|
||||
|
||||
t.Run("should filter items matching the predicate", func(t *testing.T) {
|
||||
assert.Equal(t, []int{2, 4}, moslice.Filter(ints, func(x int) bool { return x%2 == 0 }))
|
||||
assert.Equal(t, []string{"bar", "baz"}, moslice.Filter(strs, func(x string) bool { return strings.Contains(x, "b") }))
|
||||
})
|
||||
|
||||
t.Run("should moslice nil if the passed in slice is nil", func(t *testing.T) {
|
||||
assert.Nil(t, moslice.Filter(nil, func(x int) bool { return x%2 == 0 }))
|
||||
})
|
||||
|
||||
t.Run("should return empty slice if the passed in slice is empty slice", func(t *testing.T) {
|
||||
assert.Equal(t, []int{}, moslice.Filter([]int{}, func(x int) bool { return x%2 == 0 }))
|
||||
})
|
||||
}
|
||||
21
moslice/find.go
Normal file
21
moslice/find.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package moslice
|
||||
|
||||
func Contains[T comparable](ts []T, needle T) bool {
|
||||
for _, t := range ts {
|
||||
if t == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func FindWhere[T comparable](ts []T, predicate func(t T) bool) (T, bool) {
|
||||
var zeroT T
|
||||
|
||||
for _, t := range ts {
|
||||
if predicate(t) {
|
||||
return t, true
|
||||
}
|
||||
}
|
||||
return zeroT, false
|
||||
}
|
||||
25
moslice/find_test.go
Normal file
25
moslice/find_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package moslice_test
|
||||
|
||||
import (
|
||||
"lmika.dev/pkg/modash/moslice"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestContains(t *testing.T) {
|
||||
var (
|
||||
ints = []int{1, 2, 3}
|
||||
strs = []string{"a", "b", "c"}
|
||||
)
|
||||
|
||||
t.Run("should find items in the slice", func(t *testing.T) {
|
||||
assert.True(t, moslice.Contains(ints, 2))
|
||||
assert.True(t, moslice.Contains(strs, "c"))
|
||||
})
|
||||
|
||||
t.Run("should return false if items not in slice", func(t *testing.T) {
|
||||
assert.False(t, moslice.Contains(ints, 131))
|
||||
assert.False(t, moslice.Contains(strs, "bla"))
|
||||
})
|
||||
}
|
||||
19
moslice/flatten.go
Normal file
19
moslice/flatten.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package moslice
|
||||
|
||||
func Flatten[T any](tss [][]T) []T {
|
||||
if len(tss) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
entireLen := 0
|
||||
for _, ts := range tss {
|
||||
entireLen += len(ts)
|
||||
}
|
||||
|
||||
newTs := make([]T, 0, entireLen)
|
||||
for _, ts := range tss {
|
||||
newTs = append(newTs, ts...)
|
||||
}
|
||||
|
||||
return newTs
|
||||
}
|
||||
13
moslice/foreach.go
Normal file
13
moslice/foreach.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package moslice
|
||||
|
||||
// ForEachWithError runs the passed in function for each element of T. If an error
|
||||
// is encountered, the error is returned immediately and any subsequence elements
|
||||
// will not be processed.
|
||||
func ForEachWithError[T any](ts []T, fn func(t T) error) error {
|
||||
for _, t := range ts {
|
||||
if err := fn(t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
33
moslice/map.go
Normal file
33
moslice/map.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package moslice
|
||||
|
||||
// Map returns a new slice containing the elements of ts transformed by the passed in function.
|
||||
func Map[T, U any](ts []T, fn func(t T) U) (us []U) {
|
||||
if ts == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
us = make([]U, len(ts))
|
||||
for i, t := range ts {
|
||||
us[i] = fn(t)
|
||||
}
|
||||
return us
|
||||
}
|
||||
|
||||
// Map returns a new slice containing the elements of ts transformed by the passed in function, which
|
||||
// can either either a mapped value of U, or an error. If the mapping function returns an error, MapWithError
|
||||
// will return nil and the returned error.
|
||||
func MapWithError[T, U any](ts []T, fn func(t T) (U, error)) (us []U, err error) {
|
||||
if ts == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
us = make([]U, len(ts))
|
||||
for i, t := range ts {
|
||||
var e error
|
||||
us[i], e = fn(t)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
return us, nil
|
||||
}
|
||||
58
moslice/map_test.go
Normal file
58
moslice/map_test.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package moslice_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"lmika.dev/pkg/modash/moslice"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
t.Run("should return a mapped slice", func(t *testing.T) {
|
||||
ts := []int{1, 2, 3}
|
||||
us := moslice.Map(ts, func(x int) int { return x + 2 })
|
||||
|
||||
assert.Equal(t, []int{3, 4, 5}, us)
|
||||
})
|
||||
|
||||
t.Run("should return nil if passed in nil", func(t *testing.T) {
|
||||
ts := []int(nil)
|
||||
us := moslice.Map(ts, func(x int) int { return x + 2 })
|
||||
|
||||
assert.Nil(t, us)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMapWithError(t *testing.T) {
|
||||
t.Run("should return a mapped slice with no error", func(t *testing.T) {
|
||||
ts := []int{1, 2, 3}
|
||||
|
||||
us, err := moslice.MapWithError(ts, func(x int) (int, error) { return x + 2, nil })
|
||||
|
||||
assert.Equal(t, []int{3, 4, 5}, us)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("should return nil with an error when mapping function returns an error", func(t *testing.T) {
|
||||
ts := []int{1, 2, 3}
|
||||
|
||||
us, err := moslice.MapWithError(ts, func(x int) (int, error) {
|
||||
if x == 2 {
|
||||
return 0, errors.New("bang")
|
||||
}
|
||||
return x + 2, nil
|
||||
})
|
||||
|
||||
assert.Nil(t, us)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("should return nil if passed in nil", func(t *testing.T) {
|
||||
ts := []int(nil)
|
||||
us, err := moslice.MapWithError(ts, func(x int) (int, error) { return x + 2, nil })
|
||||
|
||||
assert.Nil(t, us)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
19
moslice/uniq.go
Normal file
19
moslice/uniq.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package moslice
|
||||
|
||||
func Uniq[T comparable](ts []T) []T {
|
||||
if len(ts) < 2 {
|
||||
return ts
|
||||
}
|
||||
|
||||
outT := make([]T, 0)
|
||||
seenT := make(map[T]struct{})
|
||||
|
||||
for _, t := range ts {
|
||||
if _, ok := seenT[t]; !ok {
|
||||
outT = append(outT, t)
|
||||
seenT[t] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return outT
|
||||
}
|
||||
31
moslice/uniq_test.go
Normal file
31
moslice/uniq_test.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package moslice_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"lmika.dev/pkg/modash/moslice"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUniq(t *testing.T) {
|
||||
t.Run("should return a slice with unique elements", func(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
in []int
|
||||
want []int
|
||||
}{
|
||||
{in: nil, want: nil},
|
||||
{in: []int{}, want: []int{}},
|
||||
{in: []int{2}, want: []int{2}},
|
||||
{in: []int{1, 2}, want: []int{1, 2}},
|
||||
{in: []int{2, 2}, want: []int{2}},
|
||||
{in: []int{3, 1, 4, 2, 3, 5, 1, 4}, want: []int{3, 1, 4, 2, 5}},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
assert.Equal(t, s.want, moslice.Uniq(s.in))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue