mirror of
https://github.com/jefferyjob/go-easy-utils.git
synced 2025-10-05 15:06:50 +08:00
feat: 增加了集合方法 差集、对称差集、交集 (#77)
This commit is contained in:
@@ -43,4 +43,13 @@ func SliceToMap[T any, K comparable](items []T, keyFunc func(T) K) map[K]T
|
|||||||
|
|
||||||
// ExtractKeys 字段提取
|
// ExtractKeys 字段提取
|
||||||
func ExtractKeys[T any, K comparable](items []T, keyFunc func(T) K) []K
|
func ExtractKeys[T any, K comparable](items []T, keyFunc func(T) K) []K
|
||||||
|
|
||||||
|
// Diff 计算差集
|
||||||
|
func Diff[T comparable](s []T, slices ...[]T) []T
|
||||||
|
|
||||||
|
// SymmetricDiff 计算对称差集
|
||||||
|
func SymmetricDiff[T comparable](slices ...[]T) []T
|
||||||
|
|
||||||
|
// Intersect 计算交集
|
||||||
|
func Intersect[T comparable](slices ...[]T) []T
|
||||||
```
|
```
|
70
sliceUtil/setof.go
Normal file
70
sliceUtil/setof.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package sliceUtil
|
||||||
|
|
||||||
|
// Diff 计算差集
|
||||||
|
func Diff[T comparable](s []T, slices ...[]T) []T {
|
||||||
|
seen := make(map[T]bool)
|
||||||
|
for _, slice := range slices {
|
||||||
|
for _, elem := range slice {
|
||||||
|
seen[elem] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []T
|
||||||
|
for _, elem := range s {
|
||||||
|
if !seen[elem] {
|
||||||
|
result = append(result, elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SymmetricDiff 计算对称差集
|
||||||
|
func SymmetricDiff[T comparable](slices ...[]T) []T {
|
||||||
|
// 判断当前元素是否存在于其他切片中
|
||||||
|
containsAllNotMe := func(item T, idx int, slices ...[]T) bool {
|
||||||
|
for i, slice := range slices {
|
||||||
|
if i == idx {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, val := range slice {
|
||||||
|
if val == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []T
|
||||||
|
for idx, slice := range slices {
|
||||||
|
for _, item := range slice {
|
||||||
|
if !containsAllNotMe(item, idx, slices...) {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UniqueSlice(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersect 计算交集
|
||||||
|
func Intersect[T comparable](slices ...[]T) []T {
|
||||||
|
// 判断元素是否存在于其他切片中
|
||||||
|
containsAll := func(item T, slices [][]T) bool {
|
||||||
|
for _, slice := range slices {
|
||||||
|
if !InSlice(item, slice) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []T
|
||||||
|
for _, item := range slices[0] {
|
||||||
|
if containsAll(item, slices[1:]) {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UniqueSlice(result)
|
||||||
|
}
|
39
sliceUtil/setof_example_test.go
Normal file
39
sliceUtil/setof_example_test.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package sliceUtil
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func ExampleDiff() {
|
||||||
|
s1 := []int64{1, 2, 5, 7}
|
||||||
|
s2 := []int64{5, 6, 7}
|
||||||
|
|
||||||
|
diff := Diff(s1, s2)
|
||||||
|
fmt.Println(diff)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 演示 SymmetricDiff 函数的用法
|
||||||
|
func ExampleSymmetricDiff() {
|
||||||
|
s1 := []int64{1, 5, 7}
|
||||||
|
s2 := []int64{5, 6, 7, 8, 9}
|
||||||
|
s3 := []int64{9, 10, 11}
|
||||||
|
|
||||||
|
diff := SymmetricDiff(s1, s2, s3)
|
||||||
|
fmt.Println(diff)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [1 6 8 10 11]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 演示 ExampleIntersect 函数的用法
|
||||||
|
func ExampleIntersect() {
|
||||||
|
s1 := []int64{1, 5, 7}
|
||||||
|
s2 := []int64{5, 6, 7, 8, 9}
|
||||||
|
|
||||||
|
diff := Intersect(s1, s2)
|
||||||
|
fmt.Println(diff)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// [5 7]
|
||||||
|
}
|
162
sliceUtil/setof_test.go
Normal file
162
sliceUtil/setof_test.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package sliceUtil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiff(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
s []int
|
||||||
|
slices [][]int
|
||||||
|
want []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "第一个切片与空切片列表的比较",
|
||||||
|
s: []int{1, 2, 3},
|
||||||
|
slices: [][]int{},
|
||||||
|
want: []int{1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "第一个切片与包含部分相同元素的切片列表的比较",
|
||||||
|
s: []int{1, 2, 3, 4, 5},
|
||||||
|
slices: [][]int{{4, 5}, {6, 7}},
|
||||||
|
want: []int{1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "第一个切片与包含全部相同元素的切片列表的比较",
|
||||||
|
s: []int{1, 2, 3},
|
||||||
|
slices: [][]int{{1, 2, 3}},
|
||||||
|
want: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "第一个切片与不包含任何相同元素的切片列表的比较",
|
||||||
|
s: []int{1, 2, 3},
|
||||||
|
slices: [][]int{{4, 5, 6}},
|
||||||
|
want: []int{1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "空切片与任何切片列表的比较",
|
||||||
|
s: []int{},
|
||||||
|
slices: [][]int{{1, 2, 3}},
|
||||||
|
want: []int{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
got := Diff(tc.s, tc.slices...)
|
||||||
|
if len(got) == 0 && len(tc.want) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tc.want) {
|
||||||
|
t.Errorf("Diff(%v, %v) = %v, want %v", tc.s, tc.slices, got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSymmetricDiff(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "两个切片计算对称差集",
|
||||||
|
slices: [][]int{{1, 2, 3}, {2, 3}},
|
||||||
|
expected: []int{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "多个切片计算对称差集",
|
||||||
|
slices: [][]int{{2, 4}, {3}, {1, 2, 3, 4, 5}},
|
||||||
|
expected: []int{1, 5},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "No Symmetric Difference",
|
||||||
|
slices: [][]int{{1, 2, 3}, {1, 2, 3}},
|
||||||
|
expected: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty Base Slice",
|
||||||
|
slices: [][]int{{}, {1, 2, 3}},
|
||||||
|
expected: []int{1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty Comparison Slices",
|
||||||
|
slices: [][]int{{1, 2, 3}, {}, {}},
|
||||||
|
expected: []int{1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "All Empty Slices",
|
||||||
|
slices: [][]int{{}, {}, {}},
|
||||||
|
expected: []int{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := SymmetricDiff(tt.slices...)
|
||||||
|
if len(got) == 0 && len(tt.expected) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.expected) {
|
||||||
|
t.Errorf("Diff() = %v, want %v", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntersect(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
slices [][]int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Single Intersection",
|
||||||
|
slices: [][]int{{1, 2, 3}, {2, 3, 4}},
|
||||||
|
expected: []int{2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple Intersections",
|
||||||
|
slices: [][]int{{2, 3, 4}, {2, 3, 5}, {1, 2, 2, 3}},
|
||||||
|
expected: []int{2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "No Intersection",
|
||||||
|
slices: [][]int{{1, 2, 3}, {4, 5, 6}},
|
||||||
|
expected: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty Base Slice",
|
||||||
|
slices: [][]int{{}, {1, 2, 3}},
|
||||||
|
expected: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty Comparison Slices",
|
||||||
|
slices: [][]int{{1, 2, 3}, {}, {}},
|
||||||
|
expected: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "All Empty Slices",
|
||||||
|
slices: [][]int{{}, {}, {}},
|
||||||
|
expected: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "All Same Elements",
|
||||||
|
slices: [][]int{{1, 2, 3}, {1, 2, 3}, {1, 2, 3}},
|
||||||
|
expected: []int{1, 2, 3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := Intersect(tt.slices...)
|
||||||
|
if len(got) == 0 && len(tt.expected) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.expected) {
|
||||||
|
t.Errorf("Intersect() = %v, want %v", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user