Skip to content

Commit daefa07

Browse files
committed
diff: make Diff() better
Turned it into a DisjunctiveUnion which is also better because it uses terminology from set theory to make things more clear. And it's just a superset of the functionality!
1 parent 87b4dd9 commit daefa07

File tree

4 files changed

+71
-32
lines changed

4 files changed

+71
-32
lines changed

diff/diff.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
11
package diff
22

3-
// Assumes that a is a superset of b and that they are sorted.
4-
// TODO need a better name.
5-
func Diff[C comparable](a []C, b []C) []C {
6-
result := make([]C, 0, len(a)-len(b))
3+
import "golang.org/x/exp/constraints"
4+
5+
// DisjunctiveUnion returns 2 slices containing the values not shared by both.
6+
// DisjunctiveUnion assumes that there are not duplicate values in the input
7+
// slices and that the input slices are sorted.
8+
func DisjunctiveUnion[C constraints.Ordered](a []C, b []C) ([]C, []C) {
9+
notInA := make([]C, 0)
10+
notInB := make([]C, 0)
11+
aIdx := 0
712
bIdx := 0
8-
for _, i := range a {
9-
if bIdx < len(b) {
10-
if i == b[bIdx] {
11-
bIdx++
12-
continue
13-
}
13+
14+
for aIdx < len(a) && bIdx < len(b) {
15+
if a[aIdx] < b[bIdx] {
16+
notInB = append(notInB, a[aIdx])
17+
aIdx++
18+
} else if a[aIdx] == b[bIdx] {
19+
aIdx++
20+
bIdx++
21+
} else {
22+
notInA = append(notInA, b[bIdx])
23+
bIdx++
1424
}
15-
result = append(result, i)
1625
}
1726

18-
return result
27+
for aIdx < len(a) {
28+
notInB = append(notInB, a[aIdx])
29+
aIdx++
30+
}
31+
32+
for bIdx < len(b) {
33+
notInA = append(notInA, b[bIdx])
34+
bIdx++
35+
}
36+
37+
return notInA, notInB
1938
}

diff/diff_test.go

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,45 @@
11
package diff
22

3-
import "testing"
3+
import (
4+
"reflect"
5+
"testing"
6+
)
47

5-
func Test_Diff(t *testing.T) {
6-
a := []int{1, 2, 3, 4, 5}
7-
b := []int{2, 4, 5}
8-
9-
allOfA := Diff[int](a, []int{})
10-
if len(allOfA) != len(a) {
11-
t.Errorf("Expected empty b to return full list, got %v", allOfA)
12-
}
13-
14-
empty := Diff([]int{}, []int{})
15-
if len(empty) != 0 {
16-
t.Errorf("Expected empty from empty / empty")
8+
func Test_DisjunctiveUnion(t *testing.T) {
9+
tests := []struct {
10+
a []int
11+
b []int
12+
notInA []int
13+
notInB []int
14+
}{
15+
{
16+
[]int{1, 2, 3, 8, 9, 10},
17+
[]int{3, 4, 5},
18+
[]int{4, 5},
19+
[]int{1, 2, 8, 9, 10},
20+
},
21+
{
22+
[]int{1, 2, 3},
23+
[]int{1, 2, 3},
24+
[]int{},
25+
[]int{},
26+
},
27+
{
28+
[]int{1, 2, 3},
29+
[]int{1, 2, 3, 4},
30+
[]int{4},
31+
[]int{},
32+
},
1733
}
1834

19-
result := Diff(a, b)
20-
if len(result) != len(a)-len(b) {
21-
t.Errorf("Expected diff of %v and %v to be %d long, but got %v", a, b, len(a)-len(b), result)
22-
}
35+
for _, test := range tests {
36+
notInA, notInB := DisjunctiveUnion(test.a, test.b)
2337

24-
same := Diff(a, a)
25-
if len(same) != 0 {
26-
t.Errorf("Expected diffing the same thing against itself to come up empty, got %v", same)
38+
if !reflect.DeepEqual(notInA, test.notInA) {
39+
t.Errorf("Expected notInA to be %v, got %v", test.notInA, notInA)
40+
}
41+
if !reflect.DeepEqual(notInB, test.notInB) {
42+
t.Errorf("Expected notInB to be %v, got %v", test.notInB, notInB)
43+
}
2744
}
2845
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/gorilla/mux v1.8.0
88
go4.org v0.0.0-20230225012048-214862532bf5
99
golang.org/x/crypto v0.10.0
10+
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
1011
)
1112

1213
require (

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
9999
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
100100
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
101101
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
102+
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
103+
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
102104
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
103105
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
104106
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

0 commit comments

Comments
 (0)