diff --git a/README.md b/README.md index a3526a46..f10ed4dd 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,7 @@ Supported intersection helpers: - [NoneBy](#noneby) - [Intersect](#intersect) - [Difference](#difference) +- [SymmetricDifference](#symmetric_difference) - [Union](#union) - [Without](#without) - [WithoutBy](#withoutby) @@ -2262,6 +2263,21 @@ left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5}) // []int{}, []int{} ``` +### SymmetricDifference + +Returns the symmetric difference between two collections. + +Returns all elements which are in either of the collections but not in both. +Does not preserve order of elements. + +```go +diff := SymmetricDifference([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 6}) +// []int{1, 3, 4, 5, 6} + +diff := SymmetricDifference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5}) +// []int{} +``` + ### Union Returns all distinct elements from given collections. Result will not change the order of elements relatively. diff --git a/intersect.go b/intersect.go index 72062450..cf77f102 100644 --- a/intersect.go +++ b/intersect.go @@ -141,6 +141,38 @@ func Difference[T comparable, Slice ~[]T](list1 Slice, list2 Slice) (Slice, Slic return left, right } +// Symmetric difference returns the elements presents in one collection +// but not in the other +// Order is not preserved +func SymmetricDifference[T comparable](list1 []T, list2 []T) []T { + diff := []T{} + + seenLeft := map[T]struct{}{} + seenRight := map[T]struct{}{} + + for _, elem := range list1 { + seenLeft[elem] = struct{}{} + } + + for _, elem := range list2 { + seenRight[elem] = struct{}{} + } + + for _, elem := range list1 { + if _, ok := seenRight[elem]; !ok { + diff = append(diff, elem) + } + } + + for _, elem := range list2 { + if _, ok := seenLeft[elem]; !ok { + diff = append(diff, elem) + } + } + + return diff +} + // Union returns all distinct elements from given collections. // result returns will not change the order of elements relatively. func Union[T comparable, Slice ~[]T](lists ...Slice) Slice { diff --git a/intersect_test.go b/intersect_test.go index 658a15cc..a22bd27b 100644 --- a/intersect_test.go +++ b/intersect_test.go @@ -217,6 +217,21 @@ func TestDifference(t *testing.T) { is.IsType(b, allStrings, "type preserved") } +func TestSymmetricDifference(t *testing.T) { + t.Parallel() + is := assert.New(t) + + diff1 := SymmetricDifference([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 6}) + is.ElementsMatch(diff1, []int{1, 3, 4, 5, 6}) + + diff2 := SymmetricDifference([]int{1, 2, 3, 4, 5}, []int{0, 6}) + is.ElementsMatch(diff2, []int{0, 1, 2, 3, 4, 5, 6}) + + diff3 := SymmetricDifference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5}) + is.ElementsMatch(diff3, []int{}) +} + + func TestUnion(t *testing.T) { t.Parallel() is := assert.New(t)