Skip to content

Commit 2b5f1d3

Browse files
committed
CopyMapSubset
1 parent beb0095 commit 2b5f1d3

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

map.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func CompareKeys[K comparable, V any](a, b map[K]V) ([]K, []K) {
2626

2727
// MissingKeys returns the keys that are in a but not b
2828
func MissingKeys[K comparable, V any](a, b map[K]V) []K {
29+
// Pre-allocate with capacity of a since that's the maximum possible size
2930
onlyA := make([]K, 0, len(a))
3031
for k := range a {
3132
if _, ok := b[k]; !ok {
@@ -49,19 +50,47 @@ func CopyMap[K comparable, V any](m map[K]V) map[K]V {
4950
if m == nil {
5051
return nil
5152
}
52-
newM := make(map[K]V)
53+
// Pre-allocate with capacity of m
54+
newM := make(map[K]V, len(m))
5355
for k, v := range m {
5456
newM[k] = v
5557
}
5658
return newM
5759
}
5860

61+
// CopyMapSubset creates a new map containing only the specified keys from the original map
62+
func CopyMapSubset[K comparable, V any](m map[K]V, keys []K) map[K]V {
63+
if m == nil {
64+
return nil
65+
}
66+
67+
if len(m) == 0 {
68+
return make(map[K]V)
69+
}
70+
71+
// Pre-allocate with capacity of keys since that's the maximum possible size
72+
result := make(map[K]V, len(keys))
73+
for _, k := range keys {
74+
if v, ok := m[k]; ok {
75+
result[k] = v
76+
}
77+
}
78+
return result
79+
}
80+
5981
// Merge copies b onto a, overriding any common keys:
6082
// a is modified and returned.
6183
func Merge[K comparable, V any](a, b map[K]V) map[K]V {
6284
if a == nil {
6385
return b
6486
}
87+
// If a is nil, create a new map with capacity of b
88+
if a == nil {
89+
if b == nil {
90+
return nil
91+
}
92+
a = make(map[K]V, len(b))
93+
}
6594
for k, v := range b {
6695
a[k] = v
6796
}

map_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,119 @@ func TestCopyMap(t *testing.T) {
344344
})
345345
}
346346

347+
func TestCopyMapSubset(t *testing.T) {
348+
t.Parallel()
349+
350+
t.Run("copies specified keys", func(t *testing.T) {
351+
t.Parallel()
352+
353+
original := map[string]int{
354+
"a": 1,
355+
"b": 2,
356+
"c": 3,
357+
"d": 4,
358+
}
359+
360+
keys := []string{"a", "c"}
361+
subset := generic.CopyMapSubset(original, keys)
362+
363+
t.Log("Should copy only specified keys from the original map")
364+
assert.Len(t, subset, 2)
365+
assert.Equal(t, 1, subset["a"])
366+
assert.Equal(t, 3, subset["c"])
367+
assert.NotContains(t, subset, "b")
368+
assert.NotContains(t, subset, "d")
369+
})
370+
371+
t.Run("ignores keys not in original map", func(t *testing.T) {
372+
t.Parallel()
373+
374+
original := map[string]int{
375+
"a": 1,
376+
"b": 2,
377+
}
378+
379+
keys := []string{"a", "c", "d"}
380+
subset := generic.CopyMapSubset(original, keys)
381+
382+
t.Log("Should only copy keys that exist in the original map")
383+
assert.Len(t, subset, 1)
384+
assert.Equal(t, 1, subset["a"])
385+
assert.NotContains(t, subset, "c")
386+
assert.NotContains(t, subset, "d")
387+
})
388+
389+
t.Run("returns empty map for nil input", func(t *testing.T) {
390+
t.Parallel()
391+
392+
var original map[string]int = nil
393+
keys := []string{"a", "b"}
394+
subset := generic.CopyMapSubset(original, keys)
395+
396+
t.Log("Should return empty map when input map is nil")
397+
assert.Nil(t, subset)
398+
})
399+
400+
t.Run("handles empty keys", func(t *testing.T) {
401+
t.Parallel()
402+
403+
original := map[string]int{
404+
"a": 1,
405+
"b": 2,
406+
}
407+
408+
var keys []string = nil
409+
subset := generic.CopyMapSubset(original, keys)
410+
411+
t.Log("Should return empty map when keys slice is nil")
412+
assert.Empty(t, subset)
413+
assert.NotNil(t, subset)
414+
415+
keys = []string{}
416+
subset = generic.CopyMapSubset(original, keys)
417+
418+
t.Log("Should return empty map when keys slice is empty")
419+
assert.Empty(t, subset)
420+
assert.NotNil(t, subset)
421+
})
422+
423+
t.Run("handles empty map", func(t *testing.T) {
424+
t.Parallel()
425+
426+
original := make(map[string]int)
427+
keys := []string{"a", "b"}
428+
subset := generic.CopyMapSubset(original, keys)
429+
430+
t.Log("Should return empty map when input map is empty")
431+
assert.Empty(t, subset)
432+
assert.NotNil(t, subset)
433+
})
434+
435+
t.Run("handles complex values", func(t *testing.T) {
436+
t.Parallel()
437+
438+
type User struct {
439+
Name string
440+
Admin bool
441+
}
442+
443+
original := map[string]User{
444+
"a": {Name: "Alice", Admin: false},
445+
"b": {Name: "Bob", Admin: true},
446+
"c": {Name: "Charlie", Admin: false},
447+
}
448+
449+
keys := []string{"a", "c"}
450+
subset := generic.CopyMapSubset(original, keys)
451+
452+
t.Log("Should work with complex value types")
453+
assert.Len(t, subset, 2)
454+
assert.Equal(t, User{Name: "Alice", Admin: false}, subset["a"])
455+
assert.Equal(t, User{Name: "Charlie", Admin: false}, subset["c"])
456+
assert.NotContains(t, subset, "b")
457+
})
458+
}
459+
347460
func TestMerge(t *testing.T) {
348461
t.Parallel()
349462

set.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package generic
22

33
func ToSet[T comparable](slice []T) map[T]struct{} {
4-
m := make(map[T]struct{})
4+
m := make(map[T]struct{}, len(slice))
55
for _, item := range slice {
66
m[item] = struct{}{}
77
}

0 commit comments

Comments
 (0)