Skip to content

Commit

Permalink
feat: preserve type alias of slices and maps (see #365)
Browse files Browse the repository at this point in the history
  • Loading branch information
samber committed Jun 29, 2024
1 parent de419c7 commit 3ba93a1
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 94 deletions.
22 changes: 11 additions & 11 deletions find.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value

// FindUniques returns a slice with all the unique elements of the collection.
// The order of result values is determined by the order they occur in the collection.
func FindUniques[T comparable](collection []T) []T {
func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice {
isDupl := make(map[T]bool, len(collection))

for i := range collection {
Expand All @@ -121,7 +121,7 @@ func FindUniques[T comparable](collection []T) []T {
}
}

result := make([]T, 0, len(collection)-len(isDupl))
result := make(Slice, 0, len(collection)-len(isDupl))

for i := range collection {
if duplicated := isDupl[collection[i]]; !duplicated {
Expand All @@ -135,7 +135,7 @@ func FindUniques[T comparable](collection []T) []T {
// FindUniquesBy returns a slice with all the unique elements of the collection.
// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is
// invoked for each element in array to generate the criterion by which uniqueness is computed.
func FindUniquesBy[T any, U comparable](collection []T, iteratee func(item T) U) []T {
func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
isDupl := make(map[U]bool, len(collection))

for i := range collection {
Expand All @@ -149,7 +149,7 @@ func FindUniquesBy[T any, U comparable](collection []T, iteratee func(item T) U)
}
}

result := make([]T, 0, len(collection)-len(isDupl))
result := make(Slice, 0, len(collection)-len(isDupl))

for i := range collection {
key := iteratee(collection[i])
Expand All @@ -164,7 +164,7 @@ func FindUniquesBy[T any, U comparable](collection []T, iteratee func(item T) U)

// FindDuplicates returns a slice with the first occurrence of each duplicated elements of the collection.
// The order of result values is determined by the order they occur in the collection.
func FindDuplicates[T comparable](collection []T) []T {
func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice {
isDupl := make(map[T]bool, len(collection))

for i := range collection {
Expand All @@ -176,7 +176,7 @@ func FindDuplicates[T comparable](collection []T) []T {
}
}

result := make([]T, 0, len(collection)-len(isDupl))
result := make(Slice, 0, len(collection)-len(isDupl))

for i := range collection {
if duplicated := isDupl[collection[i]]; duplicated {
Expand All @@ -191,7 +191,7 @@ func FindDuplicates[T comparable](collection []T) []T {
// FindDuplicatesBy returns a slice with the first occurrence of each duplicated elements of the collection.
// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is
// invoked for each element in array to generate the criterion by which uniqueness is computed.
func FindDuplicatesBy[T any, U comparable](collection []T, iteratee func(item T) U) []T {
func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
isDupl := make(map[U]bool, len(collection))

for i := range collection {
Expand All @@ -205,7 +205,7 @@ func FindDuplicatesBy[T any, U comparable](collection []T, iteratee func(item T)
}
}

result := make([]T, 0, len(collection)-len(isDupl))
result := make(Slice, 0, len(collection)-len(isDupl))

for i := range collection {
key := iteratee(collection[i])
Expand Down Expand Up @@ -438,12 +438,12 @@ func Sample[T any](collection []T) T {
}

// Samples returns N random unique items from collection.
func Samples[T any](collection []T, count int) []T {
func Samples[T any, Slice ~[]T](collection Slice, count int) Slice {
size := len(collection)

copy := append([]T{}, collection...)
copy := append(Slice{}, collection...)

results := []T{}
results := Slice{}

for i := 0; i < size && i < count; i++ {
copyLength := size - i
Expand Down
29 changes: 29 additions & 0 deletions find_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ func TestFindUniques(t *testing.T) {

is.Equal(0, len(result4))
is.Equal([]int{}, result4)

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := FindUniques(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}

func TestFindUniquesBy(t *testing.T) {
Expand Down Expand Up @@ -217,6 +222,13 @@ func TestFindUniquesBy(t *testing.T) {

is.Equal(0, len(result4))
is.Equal([]int{}, result4)

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := FindUniquesBy(allStrings, func(i string) string {
return i
})
is.IsType(nonempty, allStrings, "type preserved")
}

func TestFindDuplicates(t *testing.T) {
Expand All @@ -237,6 +249,11 @@ func TestFindDuplicates(t *testing.T) {

is.Equal(0, len(result3))
is.Equal([]int{}, result3)

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := FindDuplicates(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}

func TestFindDuplicatesBy(t *testing.T) {
Expand All @@ -263,6 +280,13 @@ func TestFindDuplicatesBy(t *testing.T) {

is.Equal(0, len(result3))
is.Equal([]int{}, result3)

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := FindDuplicatesBy(allStrings, func(i string) string {
return i
})
is.IsType(nonempty, allStrings, "type preserved")
}

func TestMin(t *testing.T) {
Expand Down Expand Up @@ -488,4 +512,9 @@ func TestSamples(t *testing.T) {

is.Equal(result1, []string{"a", "b", "c"})
is.Equal(result2, []string{})

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Samples(allStrings, 2)
is.IsType(nonempty, allStrings, "type preserved")
}
33 changes: 13 additions & 20 deletions intersect.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ func NoneBy[T any](collection []T, predicate func(item T) bool) bool {
}

// Intersect returns the intersection between two collections.
func Intersect[T comparable](list1 []T, list2 []T) []T {
result := []T{}
func Intersect[T comparable, Slice ~[]T](list1 Slice, list2 Slice) Slice {
result := Slice{}
seen := map[T]struct{}{}

for i := range list1 {
Expand All @@ -111,9 +111,9 @@ func Intersect[T comparable](list1 []T, list2 []T) []T {
// Difference returns the difference between two collections.
// The first value is the collection of element absent of list2.
// The second value is the collection of element absent of list1.
func Difference[T comparable](list1 []T, list2 []T) ([]T, []T) {
left := []T{}
right := []T{}
func Difference[T comparable, Slice ~[]T](list1 Slice, list2 Slice) (Slice, Slice) {
left := Slice{}
right := Slice{}

seenLeft := map[T]struct{}{}
seenRight := map[T]struct{}{}
Expand Down Expand Up @@ -143,14 +143,14 @@ func Difference[T comparable](list1 []T, list2 []T) ([]T, []T) {

// Union returns all distinct elements from given collections.
// result returns will not change the order of elements relatively.
func Union[T comparable](lists ...[]T) []T {
func Union[T comparable, Slice ~[]T](lists ...Slice) Slice {
var capLen int

for _, list := range lists {
capLen += len(list)
}

result := make([]T, 0, capLen)
result := make(Slice, 0, capLen)
seen := make(map[T]struct{}, capLen)

for i := range lists {
Expand All @@ -166,8 +166,8 @@ func Union[T comparable](lists ...[]T) []T {
}

// Without returns slice excluding all given values.
func Without[T comparable](collection []T, exclude ...T) []T {
result := make([]T, 0, len(collection))
func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) Slice {
result := make(Slice, 0, len(collection))
for i := range collection {
if !Contains(exclude, collection[i]) {
result = append(result, collection[i])
Expand All @@ -177,15 +177,8 @@ func Without[T comparable](collection []T, exclude ...T) []T {
}

// WithoutEmpty returns slice excluding empty values.
func WithoutEmpty[T comparable](collection []T) []T {
var empty T

result := make([]T, 0, len(collection))
for i := range collection {
if collection[i] != empty {
result = append(result, collection[i])
}
}

return result
//
// Deprecated: Use lo.Compact instead.
func WithoutEmpty[T comparable, Slice ~[]T](collection Slice) Slice {
return Compact(collection)
}
26 changes: 26 additions & 0 deletions intersect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ func TestIntersect(t *testing.T) {
is.Equal(result3, []int{})
is.Equal(result4, []int{0})
is.Equal(result5, []int{0})

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Intersect(allStrings, allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}

func TestDifference(t *testing.T) {
Expand All @@ -204,6 +209,12 @@ func TestDifference(t *testing.T) {
left3, right3 := Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5})
is.Equal(left3, []int{})
is.Equal(right3, []int{})

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
a, b := Difference(allStrings, allStrings)
is.IsType(a, allStrings, "type preserved")
is.IsType(b, allStrings, "type preserved")
}

func TestUnion(t *testing.T) {
Expand Down Expand Up @@ -231,6 +242,11 @@ func TestUnion(t *testing.T) {
is.Equal(result13, []int{0, 1, 2, 3, 4, 5})
is.Equal(result14, []int{0, 1, 2})
is.Equal(result15, []int{})

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Union(allStrings, allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}

func TestWithout(t *testing.T) {
Expand All @@ -247,6 +263,11 @@ func TestWithout(t *testing.T) {
is.Equal(result3, []int{})
is.Equal(result4, []int{})
is.Equal(result5, []int{})

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := Without(allStrings, "")
is.IsType(nonempty, allStrings, "type preserved")
}

func TestWithoutEmpty(t *testing.T) {
Expand All @@ -259,4 +280,9 @@ func TestWithoutEmpty(t *testing.T) {
is.Equal(result1, []int{1, 2})
is.Equal(result2, []int{1, 2})
is.Equal(result3, []int{})

type myStrings []string
allStrings := myStrings{"", "foo", "bar"}
nonempty := WithoutEmpty(allStrings)
is.IsType(nonempty, allStrings, "type preserved")
}
28 changes: 14 additions & 14 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func ValueOr[K comparable, V any](in map[K]V, key K, fallback V) V {

// PickBy returns same map type filtered by given predicate.
// Play: https://go.dev/play/p/kdg8GR_QMmf
func PickBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) map[K]V {
r := map[K]V{}
func PickBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) Map {
r := Map{}
for k := range in {
if predicate(k, in[k]) {
r[k] = in[k]
Expand All @@ -54,8 +54,8 @@ func PickBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool

// PickByKeys returns same map type filtered by given keys.
// Play: https://go.dev/play/p/R1imbuci9qU
func PickByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {
r := map[K]V{}
func PickByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map {
r := Map{}
for i := range keys {
if v, ok := in[keys[i]]; ok {
r[keys[i]] = v
Expand All @@ -66,8 +66,8 @@ func PickByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {

// PickByValues returns same map type filtered by given values.
// Play: https://go.dev/play/p/1zdzSvbfsJc
func PickByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V {
r := map[K]V{}
func PickByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) Map {
r := Map{}
for k := range in {
if Contains(values, in[k]) {
r[k] = in[k]
Expand All @@ -78,8 +78,8 @@ func PickByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V {

// OmitBy returns same map type filtered by given predicate.
// Play: https://go.dev/play/p/EtBsR43bdsd
func OmitBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool) map[K]V {
r := map[K]V{}
func OmitBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) Map {
r := Map{}
for k := range in {
if !predicate(k, in[k]) {
r[k] = in[k]
Expand All @@ -90,8 +90,8 @@ func OmitBy[K comparable, V any](in map[K]V, predicate func(key K, value V) bool

// OmitByKeys returns same map type filtered by given keys.
// Play: https://go.dev/play/p/t1QjCrs-ysk
func OmitByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {
r := map[K]V{}
func OmitByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map {
r := Map{}
for k := range in {
r[k] = in[k]
}
Expand All @@ -103,8 +103,8 @@ func OmitByKeys[K comparable, V any](in map[K]V, keys []K) map[K]V {

// OmitByValues returns same map type filtered by given values.
// Play: https://go.dev/play/p/9UYZi-hrs8j
func OmitByValues[K comparable, V comparable](in map[K]V, values []V) map[K]V {
r := map[K]V{}
func OmitByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) Map {
r := Map{}
for k := range in {
if !Contains(values, in[k]) {
r[k] = in[k]
Expand Down Expand Up @@ -170,8 +170,8 @@ func Invert[K comparable, V comparable](in map[K]V) map[V]K {

// Assign merges multiple maps from left to right.
// Play: https://go.dev/play/p/VhwfJOyxf5o
func Assign[K comparable, V any](maps ...map[K]V) map[K]V {
out := map[K]V{}
func Assign[K comparable, V any, Map ~map[K]V](maps ...Map) Map {
out := Map{}

for i := range maps {
for k := range maps[i] {
Expand Down
Loading

0 comments on commit 3ba93a1

Please sign in to comment.