下面的工具主要是计算交集和补集的

避免代码里面单独写出长长的嵌套逻辑,容易遗漏的问题

计算集合

Left (Slice)和Right(Slice)

或者Left (Map)和Right(Map)

的补集和交集

leftComplementary = Left - Right

rightComplementary = Right - Left

intersection = Left \bigcap Right

补集,交集
补集,交集
下面的工具用来计算两个slice的补集,交集

是通用的计算代码,可以使用自定义的struct类型的slice数组


import (
	"reflect"
)

type Pair struct {
	Left  interface{}
	Right interface{}
}
type EqFunc func(leftItem interface{}, rightItem interface{}) bool

func DiffSliceCal(left, right interface{}, eq EqFunc) (leftComplementary, rightComplementary interface{}, intersection []Pair) {

	checkSliceType(left, "left")
	checkSliceType(right, "right")
	leftSlice := reflect.ValueOf(left)
	rightSlice := reflect.ValueOf(right)

	intersection = make([]Pair, 0)
	eqLeftMap := map[interface{}]int{}
	eqRightMap := map[interface{}]int{}

	for i := 0; i < leftSlice.Len(); i++ {
		leftItem := leftSlice.Index(i).Interface()
		for j := 0; j < rightSlice.Len(); j++ {
			rightItem := rightSlice.Index(j).Interface()
			if eq(leftItem, rightItem) {
				intersection = append(intersection, Pair{
					Left:  leftItem,
					Right: rightItem,
				})
				eqLeftMap[leftItem] = 1
				eqRightMap[rightItem] = 1
			}
		}
	}

	leftComplementary = appendNotEqual(leftSlice, eqLeftMap)
	rightComplementary = appendNotEqual(rightSlice, eqRightMap)
	return
}

func appendNotEqual(sliceValue reflect.Value, eqMap map[interface{}]int) (ret interface{}) {
	retSliceValue := reflect.MakeSlice(sliceValue.Type(), 0, sliceValue.Len())

	for i := 0; i < sliceValue.Len(); i++ {
		leftItemValue := sliceValue.Index(i)
		leftItem := leftItemValue.Interface()
		if _, exists := eqMap[leftItem]; !exists {
			retSliceValue = reflect.Append(retSliceValue, leftItemValue)
		}
	}
	ret = retSliceValue.Interface()
	return
}

func checkSliceType(obj interface{}, prefix string) {
	if reflect.TypeOf(obj).Kind() != reflect.Slice {
		panic(prefix + " type not a slice ")
	}
}

测试代码:


import (
	"fmt"
	"testing"
)

type FieldTest struct {
	FieldID int64 `json:"field_id"`
	Scope   int   `json:"scope"`
}

func Test_diffCal(t *testing.T) {

	left := []FieldTest{{FieldID: 1}}
	right := []FieldTest{{FieldID: 1}}

	eqFuncTest := func(leftItem interface{}, rightItem interface{}) bool {

		l := leftItem.(FieldTest)
		r := rightItem.(FieldTest)

		return l.FieldID == r.FieldID
	}
	leftComplementary, rightComplementary, intersection := DiffSliceCal(left, right, eqFuncTest)
	fmt.Printf("leftComplementary:%v  \n, rightComplementary:%v  \n, intersection:%v \n-------------- \n", leftComplementary, rightComplementary, intersection)

	left = []FieldTest{{FieldID: 1, Scope: 1}, {FieldID: 2, Scope: 1}, {FieldID: 3, Scope: 2}}
	right = []FieldTest{{FieldID: 1}, {FieldID: 2, Scope: 1}, {FieldID: 4, Scope: 2}}
	leftComplementary, rightComplementary, intersection = DiffSliceCal(left, right, eqFuncTest)
	fmt.Printf("leftComplementary:%v  \n, rightComplementary:%v  \n, intersection:%v \n-------------- \n", leftComplementary, rightComplementary, intersection)
}

上面的可以通用工具,可以使用自定义类型,eq方法,就可以使用了。

可以返回leftComplementary left集合补集, rightComplementary :right 集合补集,

intersection:left和right的交集(同时返回left和right的元素对比,方便后续处理)

运行结果如下:

=== RUN   Test_diffCal
leftComplementary:[]  
, rightComplementary:[]  
, intersection:[{{1 0} {1 0}}] 
-------------- 
leftComplementary:[{3 2}]  
, rightComplementary:[{4 2}]  
, intersection:[{{1 1} {1 0}} {{2 1} {2 1}}] 
-------------- 
--- PASS: Test_diffCal (0.00s)
PASS
计算两个map的交集,补集

计算两个map 相同key的交集,补集

import "reflect"


type PairMap struct {
	left  map[interface{}]interface{}
	right map[interface{}]interface{}
}
type EqFunc func(leftItem interface{}, rightItem interface{}) bool

func DiffMapCal(left interface{}, right interface{}, keyEqFunc EqFunc) (leftComplementary, rightComplementary interface{}, intersection PairMap) {
	checkMapType(left, "left")
	checkMapType(right, "right")

	leftMap := reflect.ValueOf(left)
	rightMap := reflect.ValueOf(right)

	itLeft := leftMap.MapRange()
	itRight := rightMap.MapRange()

	eqLeftMap := map[interface{}]int{}
	eqRightMap := map[interface{}]int{}

	intersection.left = map[interface{}]interface{}{}
	intersection.right = map[interface{}]interface{}{}
	for itLeft.Next() {
		leftKey := itLeft.Key().Interface()
		for itRight.Next() {
			rightKey := itRight.Key().Interface()
			if keyEqFunc(leftKey, rightKey) {
				eqLeftMap[leftKey] = 1
				eqRightMap[rightKey] = 1

				intersection.left[leftKey] = itLeft.Value().Interface()
				intersection.right[rightKey] = itRight.Value().Interface()
			}
		}
		itRight = rightMap.MapRange()

	}

	leftComplementary = calNotEqualMap(leftMap, eqLeftMap)
	rightComplementary = calNotEqualMap(rightMap, eqRightMap)
	return
}

func calNotEqualMap(m reflect.Value, eqKeyMap map[interface{}]int) (ret interface{}) {
	retMap := make(map[interface{}]interface{}, 0)
	it := m.MapRange()
	for it.Next() {
		itemKey := it.Key().Interface()
		itemValue := it.Value().Interface()

		if _, exists := eqKeyMap[itemKey]; !exists {
			retMap[itemKey] = itemValue
		}
	}
	ret = retMap
	return
}

测试用例:


import (
	"fmt"
	"testing"
)

type FieldTest struct {
	FieldID int64 `json:"field_id"`
	Scope   int   `json:"scope"`
}

func Test_DiffMapCal(t *testing.T) {
	left := map[int64]FieldTest{20001: {FieldID: 1}}
	right := map[int64]FieldTest{20001: {FieldID: 1}}

	eqFuncTest := func(leftItem interface{}, rightItem interface{}) bool {

		l := leftItem.(int64)
		r := rightItem.(int64)

		return l == r
	}
	leftComplementary, rightComplementary, intersection := DiffMapCal(left, right, eqFuncTest)
	fmt.Printf("leftComplementary:%v  \n, rightComplementary:%v  \n, intersection:%v \n-------------- \n", leftComplementary, rightComplementary, intersection)

	left = map[int64]FieldTest{20001: {FieldID: 1, Scope: 1}, 20002: {FieldID: 2, Scope: 1}, 20003: {FieldID: 3, Scope: 2}}
	right = map[int64]FieldTest{20001: {FieldID: 1}, 20002: {FieldID: 2, Scope: 1}, 20004: {FieldID: 4, Scope: 2}}
	leftComplementary, rightComplementary, intersection = DiffMapCal(left, right, eqFuncTest)
	fmt.Printf("leftComplementary:%v  \n, rightComplementary:%v  \n, intersection:%v \n-------------- \n", leftComplementary, rightComplementary, intersection)
}

运行结果如下:

=== RUN   Test_DiffMapCal
leftComplementary:map[]  
, rightComplementary:map[]  
, intersection:{map[20001:{1 0}] map[20001:{1 0}]} 
-------------- 
leftComplementary:map[20003:{3 2}]  
, rightComplementary:map[20004:{4 2}]  
, intersection:{map[20001:{1 1} 20002:{2 1}] map[20001:{1 0} 20002:{2 1}]} 
-------------- 
--- PASS: Test_DiffMapCal (0.00s)
PASS

进一步针对for循环的优化可以修改为:

使用map替换原来的两层for循环


import "reflect"


type PairMap struct {
	left  map[interface{}]interface{}
	right map[interface{}]interface{}
}


func DiffMapCal(left interface{}, right interface{}) (leftComplementary, rightComplementary interface{}, intersection PairMap) {
	checkMapType(left, "left")
	checkMapType(right, "right")

	leftMap := reflect.ValueOf(left)
	rightMap := reflect.ValueOf(right)

	itLeft := leftMap.MapRange()
	itRight := rightMap.MapRange()

	eqMap := map[interface{}]int{}

	intersection.left = map[interface{}]interface{}{}
	intersection.right = map[interface{}]interface{}{}

	rightKeyValueMap := map[interface{}]interface{}{}
	for itRight.Next() {
		rightKey := itRight.Key().Interface()
		rightKeyValueMap[rightKey] = itRight.Value().Interface()
	}

	for itLeft.Next() {
		key := itLeft.Key().Interface()
		var rightValue interface{}
		var ok bool
		if rightValue, ok = rightKeyValueMap[key]; !ok {
			continue
		}

		eqMap[key] = 1

		intersection.left[key] = itLeft.Value().Interface()
		intersection.right[key] = rightValue
	}

	leftComplementary = calNotEqualMap(leftMap, eqMap)
	rightComplementary = calNotEqualMap(rightMap, eqMap)
	return
}

测试结果:

func Test_DiffMapCal(t *testing.T) {
	left := map[int64]FieldTest{20001: {FieldID: 1}}
	right := map[int64]FieldTest{20001: {FieldID: 1}}

	leftComplementary, rightComplementary, intersection := DiffMapCal(left, right)
	fmt.Printf("leftComplementary:%v  \n, rightComplementary:%v  \n, intersection:%v \n-------------- \n", leftComplementary, rightComplementary, intersection)

	left = map[int64]FieldTest{20001: {FieldID: 1, Scope: 1}, 20002: {FieldID: 2, Scope: 1}, 20003: {FieldID: 3, Scope: 2}}
	right = map[int64]FieldTest{20001: {FieldID: 1}, 20002: {FieldID: 2, Scope: 1}, 20004: {FieldID: 4, Scope: 2}}
	leftComplementary, rightComplementary, intersection = DiffMapCal(left, right)
	fmt.Printf("leftComplementary:%v  \n, rightComplementary:%v  \n, intersection:%v \n-------------- \n", leftComplementary, rightComplementary, intersection)
}

计算结果:

=== RUN   Test_DiffMapCal
leftComplementary:map[]  
, rightComplementary:map[]  
, intersection:{map[20001:{1 0}] map[20001:{1 0}]} 
-------------- 
leftComplementary:map[20003:{3 2}]  
, rightComplementary:map[20004:{4 2}]  
, intersection:{map[20001:{1 1} 20002:{2 1}] map[20001:{1 0} 20002:{2 1}]} 
-------------- 
--- PASS: Test_DiffMapCal (0.00s)
PASS
其他:

开源set包