假设我有很多自定义结构,
type MyStruct struct{
Name string
}
type MyStruct2 struct{
Port int
}
type MyStruct3 struct{
Person MyStruct
}
// ..other custom structs
我想有一个功能来实现这样的事情:
package main
import (
"reflect"
)
func main(){
A1s := []MyStruct{}{
{
Name: "a1",
},
{
Name: "a2",
},
}
A2s := []MyStruct{}{
{
Name: "a2",
},
{
Name: "a3",
},
}
MergeSlices(A1s, A2s, func(i1 interface{}, i2 interface{}) bool {
e1 := i1.(MyStruct)
e2 := i2.(MyStruct)
if e1.Name == e2.Name{
return true
}
return false
})
}
这应该导致:
A1s := []MyStruct{}{
{
Name: "a1",
},
{
Name: "a2",
},
{
Name: "a3",
},
}
这样一个简单的函数就可以合并任何类型的自定义结构,包括 string/int,只要你在equalMatcher
func 中有自己的实现。
实现此目的的函数接受两个切片和一个 equalMatcher 函数(如 Java 比较器),它修改第一个切片,然后合并第二个切片中的元素,但唯一。
func MergeSlices(base interface{}, guest interface{}, equalMatcher func(interface{}, interface{}) bool) {
if reflect.TypeOf(base).Kind() == reflect.Slice && reflect.TypeOf(guest).Kind() == reflect.Slice {
b := reflect.ValueOf(base)
g := reflect.ValueOf(guest)
found := make([]bool, g.Len())
for i := 0; i < b.Len(); i++ {
for j := 0; j < g.Len(); j++ {
if equalMatcher(b.Index(i).Interface(), g.Index(j).Interface()) {
found[j] = true
}
}
}
bElem := reflect.ValueOf(&base).Elem()
for i := range found {
if !found[i] {
bElem.Set(reflect.Append(bElem, g.Index(i)))
}
}
}
}
但是我很恐慌:
call of reflect.Append on interface Value
我想这是因为func MergeSlices
接受interface{}
而不是[]interface{}
我希望我的MergeSlices
功能有效。可以func MergeSlices
有一个返回值。也欢迎任何第三方软件包建议。
先感谢您。
编辑:感谢@Aristofanio Garcia,我做了它有一个返回值。
func MergeSlices(base interface{}, guest interface{}, equalMatcher func(interface{}, interface{}) bool) interface{} {
if reflect.TypeOf(base).Kind() == reflect.Slice && reflect.TypeOf(guest).Kind() == reflect.Slice {
b := reflect.ValueOf(base)
g := reflect.ValueOf(guest)
found := make([]bool, g.Len())
for i := 0; i < b.Len(); i++ {
for j := 0; j < g.Len(); j++ {
if equalMatcher(b.Index(i).Interface(), g.Index(j).Interface()) {
found[j] = true
}
}
}
for i := range found {
if !found[i] {
b = reflect.Append(b, g.Index(i))
}
}
return b.Interface()
}
return base
}
以下是一些测试:
func TestMergeSlices_String(t *testing.T) {
type args struct {
base interface{}
guest interface{}
equalMatcher func(interface{}, interface{}) bool
expectedBase interface{}
}
tests := []struct {
name string
args args
}{
{
name: "string success test full merge",
args: args{
base: []string{"a", "b"},
guest: []string{"c", "d"},
equalMatcher: func(i interface{}, i2 interface{}) bool {
s1 := i.(string)
s2 := i2.(string)
if s1 == s2{
return true
}
return false
},
expectedBase :[]string{"a", "b", "c", "d"},
},
},
{
name: "string success test partial merge",
args: args{
base: []string{"a", "b", "c"},
guest: []string{"c", "d"},
equalMatcher: func(i interface{}, i2 interface{}) bool {
s1 := i.(string)
s2 := i2.(string)
if s1 == s2{
return true
}
return false
},
expectedBase :[]string{"a", "b", "c", "d"},
},
},
{
name: "string success test with empty guest",
args: args{
base: []string{"a", "b", "c"},
guest: []string{},
equalMatcher: func(i interface{}, i2 interface{}) bool {
s1 := i.(string)
s2 := i2.(string)
if s1 == s2{
return true
}
return false
},
expectedBase :[]string{"a", "b", "c"},
},
},
{
name: "string success test with empty base",
args: args{
base: []string{},
guest: []string{"a", "b", "c"},
equalMatcher: func(i interface{}, i2 interface{}) bool {
s1 := i.(string)
s2 := i2.(string)
if s1 == s2{
return true
}
return false
},
expectedBase :[]string{"a", "b", "c"},
},
},
{
name: "string success test with both empty",
args: args{
base: []string{},
guest: []string{},
equalMatcher: func(i interface{}, i2 interface{}) bool {
s1 := i.(string)
s2 := i2.(string)
if s1 == s2{
return true
}
return false
},
expectedBase :[]string{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotInterface := MergeSlices(tt.args.base, tt.args.guest, tt.args.equalMatcher)
got := gotInterface.([]string)
assert.Equal(t, tt.args.expectedBase, got)
})
}
}
func TestMergeSlices_MyStruct(t *testing.T) {
type args struct {
base interface{}
guest interface{}
equalMatcher func(interface{}, interface{}) bool
expectedBase interface{}
}
type S struct{
S1 int
}
tests := []struct {
name string
args args
}{
{
name: "int success test",
args: args{
base: []S{
{
S1: 1,
},
{
S1: 2,
},
},
guest: []S{
{
S1: 2,
},
{
S1: 3,
},
},
equalMatcher: func(i interface{}, i2 interface{}) bool {
s1 := i.(S)
s2 := i2.(S)
if s1.S1 == s2.S1{
return true
}
return false
},
expectedBase :[]S{
{
S1: 1,
},
{
S1: 2,
},
{
S1: 3,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotInterface := MergeSlices(tt.args.base, tt.args.guest, tt.args.equalMatcher)
got := gotInterface.([]S)
assert.Equal(t, tt.args.expectedBase, got)
})
}
}