initial commit
This commit is contained in:
162
slice/slice.go
Normal file
162
slice/slice.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package slice
|
||||
|
||||
import (
|
||||
"go.sour.is/pkg/math"
|
||||
)
|
||||
|
||||
// FilterType returns a subset that matches the type.
|
||||
func FilterType[T any](in ...any) []T {
|
||||
lis := make([]T, 0, len(in))
|
||||
for _, u := range in {
|
||||
if t, ok := u.(T); ok {
|
||||
lis = append(lis, t)
|
||||
}
|
||||
}
|
||||
return lis
|
||||
}
|
||||
|
||||
func FilterFn[T any](fn func(T) bool, in ...T) []T {
|
||||
lis := make([]T, 0, len(in))
|
||||
for _, t := range in {
|
||||
if fn(t) {
|
||||
lis = append(lis, t)
|
||||
}
|
||||
}
|
||||
return lis
|
||||
}
|
||||
|
||||
// Find returns the first of type found. or false if not found.
|
||||
func Find[T any](in ...any) (T, bool) {
|
||||
return First(FilterType[T](in...)...)
|
||||
}
|
||||
|
||||
func FindFn[T any](fn func(T) bool, in ...T) (T, bool) {
|
||||
return First(FilterFn(fn, in...)...)
|
||||
}
|
||||
|
||||
// First returns the first element in a slice.
|
||||
func First[T any](in ...T) (T, bool) {
|
||||
if len(in) == 0 {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
return in[0], true
|
||||
}
|
||||
|
||||
// Map applys func to each element s and returns results as slice.
|
||||
func Map[T, U any](f func(int, T) U) func(...T) []U {
|
||||
return func(lis ...T) []U {
|
||||
r := make([]U, len(lis))
|
||||
for i, v := range lis {
|
||||
r[i] = f(i, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
func Reduce[T, R any](r R, fn func(T, R, int) R) func(...T) R {
|
||||
return func(lis ...T) R {
|
||||
for i, t := range lis {
|
||||
r = fn(t, r, i)
|
||||
}
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
type Pair[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
|
||||
func FromMap[K comparable, V any](m map[K]V) (keys []K, values []V) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
keys = FromMapKeys(m)
|
||||
return keys, FromMapValues(m, keys)
|
||||
}
|
||||
|
||||
func FromMapKeys[K comparable, V any](m map[K]V) (keys []K) {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
keys = make([]K, 0, len(m))
|
||||
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
func FromMapValues[K comparable, V any](m map[K]V, keys []K) (values []V) {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
values = make([]V, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
values = append(values, m[k])
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
func ToMap[K comparable, V any](keys []K, values []V) (m map[K]V) {
|
||||
m = make(map[K]V, len(keys))
|
||||
|
||||
for i := range keys {
|
||||
if len(values) < i {
|
||||
break
|
||||
}
|
||||
m[keys[i]] = values[i]
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func Zip[K comparable, V any](k []K, v []V) []Pair[K, V] {
|
||||
lis := make([]Pair[K, V], math.Max(len(k), len(v)))
|
||||
for i := range lis {
|
||||
if k != nil && len(k) > i {
|
||||
lis[i].Key = k[i]
|
||||
}
|
||||
|
||||
if v != nil && len(v) > i {
|
||||
lis[i].Value = v[i]
|
||||
}
|
||||
}
|
||||
return lis
|
||||
}
|
||||
|
||||
func Align[T any](k []T, v []T, less func(T, T) bool) []Pair[*T, *T] {
|
||||
lis := make([]Pair[*T, *T], 0, math.Max(len(k), len(v)))
|
||||
|
||||
var j int
|
||||
|
||||
for i := 0; i < len(k); {
|
||||
if j >= len(v) || less(k[i], v[j]) {
|
||||
lis = append(lis, Pair[*T, *T]{&k[i], nil})
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if less(v[j], k[i]) {
|
||||
lis = append(lis, Pair[*T, *T]{nil, &v[j]})
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
||||
lis = append(lis, Pair[*T, *T]{&k[i], &v[j]})
|
||||
i++
|
||||
j++
|
||||
}
|
||||
for ; j < len(v); j++ {
|
||||
lis = append(lis, Pair[*T, *T]{nil, &v[j]})
|
||||
}
|
||||
|
||||
return lis
|
||||
|
||||
}
|
||||
53
slice/slice_test.go
Normal file
53
slice/slice_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package slice_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
"go.sour.is/pkg/slice"
|
||||
)
|
||||
|
||||
func TestAlign(t *testing.T) {
|
||||
type testCase struct {
|
||||
left, right []string
|
||||
combined []slice.Pair[*string, *string]
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{
|
||||
left: []string{"1", "3", "5"},
|
||||
right: []string{"2", "3", "4"},
|
||||
combined: []slice.Pair[*string, *string]{
|
||||
{ptr("1"), nil},
|
||||
{nil, ptr("2")},
|
||||
{ptr("3"), ptr("3")},
|
||||
{nil, ptr("4")},
|
||||
{ptr("5"), nil},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
left: []string{"2", "3", "4"},
|
||||
right: []string{"1", "3", "5"},
|
||||
combined: []slice.Pair[*string, *string]{
|
||||
{nil, ptr("1")},
|
||||
{ptr("2"), nil},
|
||||
{ptr("3"), ptr("3")},
|
||||
{ptr("4"), nil},
|
||||
{nil, ptr("5")},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
for _, tt := range tests {
|
||||
combined := slice.Align(tt.left, tt.right, func(l, r string) bool { return l < r })
|
||||
is.Equal(len(combined), len(tt.combined))
|
||||
for i := range combined {
|
||||
is.Equal(combined[i], tt.combined[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T { return &v }
|
||||
Reference in New Issue
Block a user