Skip to content

Commit 7932a4b

Browse files
committed
add map containment utility functions
1 parent 2420615 commit 7932a4b

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

pkg/collections/maps/utils.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package maps
22

33
import (
4+
"reflect"
5+
46
"k8s.io/utils/ptr"
57

68
"github.com/openmcp-project/controller-utils/pkg/collections/filters"
@@ -56,6 +58,36 @@ func Intersect[K comparable, V any](source map[K]V, maps ...map[K]V) map[K]V {
5658
return res
5759
}
5860

61+
// ContainsKeysWithValues checks if 'super' is a superset of 'sub', meaning that all keys of 'sub' are also present in 'super' with the same values.
62+
// Uses reflect.DeepEqual to compare the values, use ContainsMapFunc if you want to use a custom equality function.
63+
func ContainsKeysWithValues[K comparable, V any](super, sub map[K]V) bool {
64+
return ContainsKeysWithValuesFunc(super, sub, func(a, b V) bool {
65+
return reflect.DeepEqual(a, b)
66+
})
67+
}
68+
69+
// ContainsKeysWithValuesFunc checks if 'super' is a superset of 'sub', meaning that all keys of 'sub' are also present in 'super' with the same values.
70+
// The values are compared using the provided equality function.
71+
// If the equality function returns false for any key-value pair, it returns false.
72+
func ContainsKeysWithValuesFunc[K comparable, V any](super, sub map[K]V, equal func(a, b V) bool) bool {
73+
for k, bv := range sub {
74+
if av, ok := super[k]; !ok || !equal(av, bv) {
75+
return false
76+
}
77+
}
78+
return true
79+
}
80+
81+
// ContainsKeys returns true if all given keys are present in the map.
82+
func ContainsKeys[K comparable, V any](super map[K]V, keys ...K) bool {
83+
for _, k := range keys {
84+
if _, ok := super[k]; !ok {
85+
return false
86+
}
87+
}
88+
return true
89+
}
90+
5991
// GetAny returns an arbitrary key-value pair from the map as a pointer to a pairs.Pair.
6092
// If the map is empty, it returns nil.
6193
func GetAny[K comparable, V any](m map[K]V) *pairs.Pair[K, V] {

pkg/collections/maps/utils_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,71 @@ var _ = Describe("Map Utils Tests", func() {
7878

7979
})
8080

81+
Context("ContainsKeysWithValues", func() {
82+
83+
It("should return true if the second map is empty or nil", func() {
84+
m1 := map[string]string{"foo": "bar", "bar": "baz"}
85+
Expect(maps.ContainsKeysWithValues(m1, nil)).To(BeTrue())
86+
Expect(maps.ContainsKeysWithValues(m1, map[string]string{})).To(BeTrue())
87+
Expect(maps.ContainsKeysWithValues[string, string](nil, nil)).To(BeTrue())
88+
})
89+
90+
It("should return true if both maps are identical", func() {
91+
m1 := map[string]string{"foo": "bar", "bar": "baz"}
92+
Expect(maps.ContainsKeysWithValues(m1, m1)).To(BeTrue())
93+
})
94+
95+
It("should return true if the first map contains all keys and values of the second map", func() {
96+
m1 := map[string]string{"foo": "bar", "bar": "baz", "baz": "asdf"}
97+
m2 := map[string]string{"foo": "bar", "baz": "asdf"}
98+
Expect(maps.ContainsKeysWithValues(m1, m2)).To(BeTrue())
99+
100+
m3 := map[string]string{"bar": "baz"}
101+
Expect(maps.ContainsKeysWithValues(m1, m3)).To(BeTrue())
102+
})
103+
104+
It("should return false if the first map contains all keys but has different values", func() {
105+
m1 := map[string]string{"foo": "bar", "bar": "baz"}
106+
m2 := map[string]string{"foo": "baz", "bar": "baz"}
107+
Expect(maps.ContainsKeysWithValues(m1, m2)).To(BeFalse())
108+
})
109+
110+
It("should return false if the first map does not contain all keys of the second map", func() {
111+
m1 := map[string]string{"foo": "bar"}
112+
m2 := map[string]string{"foo": "bar", "bar": "baz"}
113+
Expect(maps.ContainsKeysWithValues(m1, m2)).To(BeFalse())
114+
})
115+
116+
It("should work with a custom equality function", func() {
117+
m1 := map[string]string{"foo": "bar", "bar": "baz"}
118+
m2 := map[string]string{"foo": "xyz", "bar": "mno"}
119+
cmp := func(a, b string) bool {
120+
return len(a) == len(b)
121+
}
122+
Expect(maps.ContainsKeysWithValuesFunc(m1, m2, cmp)).To(BeTrue())
123+
})
124+
125+
})
126+
127+
Context("ContainsKeys", func() {
128+
129+
It("should return true if all keys are present", func() {
130+
m1 := map[string]string{"foo": "bar", "bar": "baz", "baz": "asdf"}
131+
Expect(maps.ContainsKeys(m1, "foo", "bar")).To(BeTrue())
132+
})
133+
134+
It("should return true if no keys are provided", func() {
135+
m1 := map[string]string{"foo": "bar", "bar": "baz", "baz": "asdf"}
136+
Expect(maps.ContainsKeys(m1)).To(BeTrue())
137+
Expect(maps.ContainsKeys[string, string](nil)).To(BeTrue())
138+
})
139+
140+
It("should return false if any key is missing", func() {
141+
m1 := map[string]string{"foo": "bar", "bar": "baz", "baz": "asdf"}
142+
Expect(maps.ContainsKeys(m1, "foo", "missing")).To(BeFalse())
143+
Expect(maps.ContainsKeys(m1, "missing")).To(BeFalse())
144+
})
145+
146+
})
147+
81148
})

0 commit comments

Comments
 (0)