Skip to content

Commit 476dfea

Browse files
committed
feat: add foreach and clear to lazymap
Adds foreach, remove, getinverse, removeinverse and clear methods to LazyMap, BiMap and LazyBiMap. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
1 parent 214c1ef commit 476dfea

File tree

2 files changed

+214
-9
lines changed

2 files changed

+214
-9
lines changed

containers/lazymap.go

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,33 @@ func (m *LazyBiMap[K, V]) Get(key K) (V, bool) {
3535
return val, ok
3636
}
3737

38+
// GetInverse returns the key for the given value.
39+
func (m *LazyBiMap[K, V]) GetInverse(value V) (K, bool) {
40+
key, ok := m.biMap.GetInverse(value)
41+
42+
return key, ok
43+
}
44+
45+
// Remove removes the value for the given key.
46+
func (m *LazyBiMap[K, V]) Remove(key K) {
47+
m.biMap.Remove(key)
48+
}
49+
50+
// RemoveInverse removes the key for the given value.
51+
func (m *LazyBiMap[K, V]) RemoveInverse(value V) {
52+
m.biMap.RemoveInverse(value)
53+
}
54+
55+
// Clear removes all values.
56+
func (m *LazyBiMap[K, V]) Clear() {
57+
m.biMap.Clear()
58+
}
59+
60+
// ForEach calls the given function for each key-value pair.
61+
func (m *LazyBiMap[K, V]) ForEach(f func(K, V)) {
62+
m.biMap.ForEach(f)
63+
}
64+
3865
// BiMap (or “bidirectional map”) is a special kind of map that maintains
3966
// an inverse view of the map while ensuring that no duplicate values are present
4067
// and a value can always be used safely to get the key back.
@@ -50,6 +77,13 @@ func (m *BiMap[K, V]) Get(key K) (V, bool) {
5077
return val, ok
5178
}
5279

80+
// GetInverse returns the key for the given value.
81+
func (m *BiMap[K, V]) GetInverse(val V) (K, bool) {
82+
key, ok := m.v2k[val]
83+
84+
return key, ok
85+
}
86+
5387
// Set sets the value for the given key.
5488
func (m *BiMap[K, V]) Set(key K, val V) {
5589
if m.k2v == nil {
@@ -66,19 +100,62 @@ func (m *BiMap[K, V]) Set(key K, val V) {
66100
m.v2k[val] = key
67101
}
68102

103+
// Remove deletes the value for the given key.
104+
func (m *BiMap[K, V]) Remove(key K) {
105+
if m.k2v == nil {
106+
return
107+
}
108+
109+
val, ok := m.k2v[key]
110+
if !ok {
111+
return
112+
}
113+
114+
delete(m.k2v, key)
115+
delete(m.v2k, val)
116+
}
117+
118+
// RemoveInverse deletes the key for the given value.
119+
func (m *BiMap[K, V]) RemoveInverse(val V) {
120+
if m.v2k == nil {
121+
return
122+
}
123+
124+
key, ok := m.v2k[val]
125+
if !ok {
126+
return
127+
}
128+
129+
delete(m.v2k, val)
130+
delete(m.k2v, key)
131+
}
132+
133+
// Clear removes all key-value pairs.
134+
func (m *BiMap[K, V]) Clear() {
135+
m.k2v = nil
136+
m.v2k = nil
137+
}
138+
139+
// ForEach calls the given function for each key-value pair.
140+
func (m *BiMap[K, V]) ForEach(f func(K, V)) {
141+
for k, v := range m.k2v {
142+
f(k, v)
143+
}
144+
}
145+
69146
// LazyMap is like usual map but creates values on demand.
70147
type LazyMap[K comparable, V comparable] struct {
71148
Creator func(K) (V, error)
72-
biMap map[K]V
149+
dataMap map[K]V
73150
}
74151

75152
// GetOrCreate returns the value for the given key. It creates it using Creator if it doesn't exist.
76153
func (m *LazyMap[K, V]) GetOrCreate(key K) (V, error) {
77-
if m.biMap == nil {
78-
m.biMap = map[K]V{}
154+
if m.dataMap == nil {
155+
m.dataMap = map[K]V{}
79156
}
80157

81-
val, ok := m.biMap[key]
158+
val, ok := m.dataMap[key]
82159
if ok {
83160
return val, nil
84161
}
@@ -88,23 +165,35 @@ func (m *LazyMap[K, V]) GetOrCreate(key K) (V, error) {
88165
return *new(V), err //nolint:gocritic
89166
}
90167

91-
m.biMap[key] = val
168+
m.dataMap[key] = val
92169

93170
return val, nil
94171
}
95172

96173
// Get returns the value for the given key.
97174
func (m *LazyMap[K, V]) Get(key K) (V, bool) {
98-
val, ok := m.biMap[key]
175+
val, ok := m.dataMap[key]
99176

100177
return val, ok
101178
}
102179

103180
// Remove deletes the value for the given key.
104181
func (m *LazyMap[K, V]) Remove(key K) {
105-
if m.biMap == nil {
182+
if m.dataMap == nil {
106183
return
107184
}
108185

109-
delete(m.biMap, key)
186+
delete(m.dataMap, key)
187+
}
188+
189+
// Clear removes all key-value pairs.
190+
func (m *LazyMap[K, V]) Clear() {
191+
m.dataMap = nil
192+
}
193+
194+
// ForEach calls the given function for each key-value pair.
195+
func (m *LazyMap[K, V]) ForEach(f func(K, V)) {
196+
for k, v := range m.dataMap {
197+
f(k, v)
198+
}
110199
}

containers/lazymap_test.go

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
44

5+
//nolint:dupl
56
package containers_test
67

78
import (
89
"fmt"
910
"testing"
1011

12+
"github.com/stretchr/testify/assert"
1113
"github.com/stretchr/testify/require"
1214

1315
"github.com/siderolabs/gen/containers"
@@ -32,9 +34,12 @@ func TestLazyBiMap(t *testing.T) {
3234
},
3335
}
3436

35-
t.Run("should return nothing if key doesnt exist", func(t *testing.T) {
37+
t.Run("should return nothing if key or value doesnt exist", func(t *testing.T) {
3638
_, ok := m.Get(0)
3739
require.False(t, ok)
40+
41+
_, ok = m.GetInverse(0)
42+
require.False(t, ok)
3843
})
3944

4045
t.Run("should create value on demand", func(t *testing.T) {
@@ -49,6 +54,13 @@ func TestLazyBiMap(t *testing.T) {
4954
require.Equal(t, 100, create)
5055
})
5156

57+
t.Run("should return existing key by value", func(t *testing.T) {
58+
key, ok := m.GetInverse(100)
59+
require.True(t, ok)
60+
61+
assert.Equal(t, 1, key)
62+
})
63+
5264
t.Run("should remove old key if has new key", func(t *testing.T) {
5365
create, err := m.GetOrCreate(11)
5466
require.NoError(t, err)
@@ -62,6 +74,67 @@ func TestLazyBiMap(t *testing.T) {
6274
_, err := m.GetOrCreate(-1)
6375
require.Error(t, err)
6476
})
77+
78+
t.Run("should remove key", func(t *testing.T) {
79+
_, err := m.GetOrCreate(12)
80+
require.NoError(t, err)
81+
82+
m.Remove(12)
83+
_, ok := m.Get(12)
84+
require.False(t, ok)
85+
})
86+
87+
t.Run("should remove key by value", func(t *testing.T) {
88+
_, err := m.GetOrCreate(13)
89+
require.NoError(t, err)
90+
91+
m.RemoveInverse(300)
92+
_, ok := m.Get(13)
93+
require.False(t, ok)
94+
})
95+
96+
t.Run("should remove all entries", func(t *testing.T) {
97+
_, err := m.GetOrCreate(14)
98+
require.NoError(t, err)
99+
100+
_, err = m.GetOrCreate(15)
101+
require.NoError(t, err)
102+
103+
m.Clear()
104+
105+
_, ok := m.Get(13)
106+
require.False(t, ok)
107+
108+
_, ok = m.Get(14)
109+
require.False(t, ok)
110+
})
111+
112+
t.Run("should iterate over entries", func(t *testing.T) {
113+
var keys []int
114+
var values []int
115+
116+
m.ForEach(func(k int, v int) {
117+
keys = append(keys, k)
118+
values = append(values, v)
119+
})
120+
121+
assert.Empty(t, keys)
122+
assert.Empty(t, values)
123+
124+
_, err := m.GetOrCreate(1)
125+
require.NoError(t, err)
126+
127+
_, err = m.GetOrCreate(2)
128+
require.NoError(t, err)
129+
130+
m.ForEach(func(k int, v int) {
131+
keys = append(keys, k)
132+
values = append(values, v)
133+
})
134+
135+
assert.Equal(t, []int{1, 2}, keys)
136+
assert.Equal(t, []int{100, 200}, values)
137+
})
65138
}
66139

67140
func TestLazyMap(t *testing.T) {
@@ -114,4 +187,47 @@ func TestLazyMap(t *testing.T) {
114187
_, ok := m.Get(1)
115188
require.False(t, ok)
116189
})
190+
191+
t.Run("should remove all entries", func(t *testing.T) {
192+
_, err := m.GetOrCreate(2)
193+
require.NoError(t, err)
194+
195+
_, err = m.GetOrCreate(3)
196+
require.NoError(t, err)
197+
198+
m.Clear()
199+
200+
_, ok := m.Get(1)
201+
require.False(t, ok)
202+
203+
_, ok = m.Get(2)
204+
require.False(t, ok)
205+
})
206+
207+
t.Run("should iterate over entries", func(t *testing.T) {
208+
var keys []int
209+
var values []int
210+
211+
m.ForEach(func(k int, v int) {
212+
keys = append(keys, k)
213+
values = append(values, v)
214+
})
215+
216+
assert.Empty(t, keys)
217+
assert.Empty(t, values)
218+
219+
_, err := m.GetOrCreate(4)
220+
require.NoError(t, err)
221+
222+
_, err = m.GetOrCreate(5)
223+
require.NoError(t, err)
224+
225+
m.ForEach(func(k int, v int) {
226+
keys = append(keys, k)
227+
values = append(values, v)
228+
})
229+
230+
assert.Equal(t, []int{4, 5}, keys)
231+
assert.Equal(t, []int{400, 500}, values)
232+
})
117233
}

0 commit comments

Comments
 (0)