Skip to content

Commit e110c89

Browse files
implement slices venn functions: Disjunction, Intersection, Union and Subtract
1 parent a5a33de commit e110c89

File tree

2 files changed

+215
-0
lines changed

2 files changed

+215
-0
lines changed

src/slices/slices.go

+75
Original file line numberDiff line numberDiff line change
@@ -566,3 +566,78 @@ func Filter[S ~[]E, E any](s S, predicate func(e E, i int) bool) S {
566566
clear(s[i:]) // zero/nil out the obsolete elements, for GC
567567
return s[:i]
568568
}
569+
570+
// Disjunction returns a slice containing the exclusive disjunction (symmetric difference) of the given slices.
571+
// This means the elements which are in either one of the slice but not in both.
572+
func Disjunction[S ~[]E, E comparable](s1, s2 S) S {
573+
ms1 := CardinalityMap(s1)
574+
ms2 := CardinalityMap(s2)
575+
r := make(S, 0, len(s1)+len(s2))
576+
for _, v := range s1 {
577+
if ms2[v] > 0 {
578+
ms1[v] -= 1
579+
ms2[v] -= 1
580+
} else {
581+
r = append(r, v)
582+
}
583+
}
584+
for _, v := range s2 {
585+
if ms2[v] > 0 {
586+
ms2[v] -= 1
587+
r = append(r, v)
588+
}
589+
}
590+
return Clip(r)
591+
}
592+
593+
// Intersection returns a slice containing the intersection of the given slices.
594+
// This means the elements which are in both the given slices.
595+
func Intersection[S ~[]E, E comparable](s1, s2 S) S {
596+
ms1 := CardinalityMap(s1)
597+
ms2 := CardinalityMap(s2)
598+
r := make(S, 0, min(len(s1), len(s2)))
599+
for _, v := range s1 {
600+
if ms2[v] > 0 {
601+
ms2[v] -= 1
602+
r = append(r, v)
603+
}
604+
ms1[v] -= 1
605+
}
606+
return Clip(r)
607+
}
608+
609+
// Union is used to do the union of the 2 provided collections.
610+
// The cardinality of each element in the returned collection will be equal to the maximum of that element in the given 2 collections.
611+
func Union[S ~[]E, E comparable](s1, s2 S) S {
612+
if len(s1) == 0 {
613+
return s2
614+
}
615+
if len(s2) == 0 {
616+
return s1
617+
}
618+
r := make(S, 0, len(s1)+len(s2))
619+
r = append(r, s1...)
620+
ms1 := CardinalityMap(s1)
621+
for _, v := range s2 {
622+
if ms1[v] <= 0 {
623+
r = append(r, v)
624+
}
625+
ms1[v] -= 1
626+
}
627+
return Clip(r)
628+
}
629+
630+
// Subtract return a new slice containing s1 - s2 i.e. all elements of s2 removed from s1.
631+
func Subtract[S ~[]E, E comparable](s1, s2 S) S {
632+
m := CardinalityMap(s2)
633+
r := make(S, 0, len(s1))
634+
for _, v := range s1 {
635+
c := m[v]
636+
if c > 0 {
637+
m[v] -= 1
638+
} else {
639+
r = append(r, v)
640+
}
641+
}
642+
return Clip(r)
643+
}

src/slices/slices_test.go

+140
Original file line numberDiff line numberDiff line change
@@ -1632,3 +1632,143 @@ func BenchmarkFilter_Large(b *testing.B) {
16321632
_ = Filter(ss, predicate)
16331633
}
16341634
}
1635+
1636+
var disjunctionTests = []struct {
1637+
s1 []int
1638+
s2 []int
1639+
want []int
1640+
}{
1641+
{
1642+
nil,
1643+
nil,
1644+
nil,
1645+
},
1646+
{
1647+
[]int{1, 5, 9, 6, 7},
1648+
[]int{2, 6, 7, 3},
1649+
[]int{1, 5, 9, 2, 3},
1650+
},
1651+
{
1652+
[]int{1, 2, 1, 3, 1, 2},
1653+
[]int{1, 2, 2, 3},
1654+
[]int{1, 1},
1655+
},
1656+
{
1657+
[]int{1, 2, 3},
1658+
[]int{1, 2, 3},
1659+
nil,
1660+
},
1661+
}
1662+
1663+
func TestDisjunction(t *testing.T) {
1664+
for _, test := range disjunctionTests {
1665+
if got := Disjunction(test.s1, test.s2); !Equal(got, test.want) {
1666+
t.Errorf("Disjunction(%v, %v) = %v, want %v", test.s1, test.s2, got, test.want)
1667+
}
1668+
}
1669+
}
1670+
1671+
var intersectionTests = []struct {
1672+
s1 []int
1673+
s2 []int
1674+
want []int
1675+
}{
1676+
{
1677+
nil,
1678+
nil,
1679+
nil,
1680+
},
1681+
{
1682+
[]int{1, 5, 9, 6, 7},
1683+
[]int{2, 6, 7, 3},
1684+
[]int{6, 7},
1685+
},
1686+
{
1687+
[]int{1, 2, 1, 3, 1, 2},
1688+
[]int{1, 2, 2, 3},
1689+
[]int{1, 2, 3, 2},
1690+
},
1691+
{
1692+
[]int{1, 2, 3},
1693+
[]int{1, 2, 3},
1694+
[]int{1, 2, 3},
1695+
},
1696+
}
1697+
1698+
func TestIntersection(t *testing.T) {
1699+
for _, test := range intersectionTests {
1700+
if got := Intersection(test.s1, test.s2); !Equal(got, test.want) {
1701+
t.Errorf("Intersection(%v, %v) = %v, want %v", test.s1, test.s2, got, test.want)
1702+
}
1703+
}
1704+
}
1705+
1706+
var unionTests = []struct {
1707+
s1 []int
1708+
s2 []int
1709+
want []int
1710+
}{
1711+
{
1712+
nil,
1713+
nil,
1714+
nil,
1715+
},
1716+
{
1717+
[]int{1, 5, 9, 6, 7},
1718+
[]int{2, 6, 7, 3},
1719+
[]int{1, 5, 9, 6, 7, 2, 3},
1720+
},
1721+
{
1722+
[]int{1, 2, 1, 3, 1, 2},
1723+
[]int{1, 2, 2, 3},
1724+
[]int{1, 2, 1, 3, 1, 2},
1725+
},
1726+
{
1727+
[]int{1, 2, 3},
1728+
[]int{1, 2, 3},
1729+
[]int{1, 2, 3},
1730+
},
1731+
}
1732+
1733+
func TestUnion(t *testing.T) {
1734+
for _, test := range unionTests {
1735+
if got := Union(test.s1, test.s2); !Equal(got, test.want) {
1736+
t.Errorf("Union(%v, %v) = %v, want %v", test.s1, test.s2, got, test.want)
1737+
}
1738+
}
1739+
}
1740+
1741+
var subtractTests = []struct {
1742+
s1 []int
1743+
s2 []int
1744+
want []int
1745+
}{
1746+
{
1747+
nil,
1748+
nil,
1749+
nil,
1750+
},
1751+
{
1752+
[]int{1, 5, 9, 6, 7},
1753+
[]int{2, 6, 7, 3},
1754+
[]int{1, 5, 9},
1755+
},
1756+
{
1757+
[]int{1, 2, 1, 3, 1, 2},
1758+
[]int{1, 2, 2, 3},
1759+
[]int{1, 1},
1760+
},
1761+
{
1762+
[]int{1, 2, 3},
1763+
[]int{1, 2, 3},
1764+
nil,
1765+
},
1766+
}
1767+
1768+
func TestSubtract(t *testing.T) {
1769+
for _, test := range subtractTests {
1770+
if got := Subtract(test.s1, test.s2); !Equal(got, test.want) {
1771+
t.Errorf("Subtract(%v, %v) = %v, want %v", test.s1, test.s2, got, test.want)
1772+
}
1773+
}
1774+
}

0 commit comments

Comments
 (0)