lang: add a helper for iterating a map in order (#18809)

In some cases it is helpful to iterate a map in the sorted order of
the maps keyset - particularly in implementations of some function for
which the tests cannot be deterministic without order.
This commit is contained in:
Seth Hoenig
2023-10-20 08:11:35 -05:00
committed by GitHub
parent 1a0d1efb0d
commit 3e8ebf85f5
2 changed files with 82 additions and 0 deletions

27
lib/lang/maps.go Normal file
View File

@@ -0,0 +1,27 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package lang
import (
"cmp"
"slices"
)
// WalkMap will call f for every k/v in m, iterating the keyset of m in the
// cmp.Ordered order. If f returns false the iteration is halted early.
func WalkMap[K cmp.Ordered, V any](m map[K]V, f func(K, V) bool) {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
// sort keys ascending
slices.Sort(keys)
for _, k := range keys {
if !f(k, m[k]) {
return // stop iteration
}
}
}

55
lib/lang/maps_test.go Normal file
View File

@@ -0,0 +1,55 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package lang
import (
"testing"
"github.com/shoenig/test/must"
)
func TestWalkMap(t *testing.T) {
m := map[int]string{
1: "one",
3: "three",
4: "four",
2: "two",
5: "five",
}
result := make([]string, 0, 5)
f := func(_ int, v string) bool {
result = append(result, v)
return true
}
WalkMap(m, f)
must.Eq(t, []string{"one", "two", "three", "four", "five"}, result)
}
func TestWalkMap_halt(t *testing.T) {
m := map[int]string{
5: "five",
1: "one",
3: "three",
4: "four",
}
result := make([]string, 0, 3)
f := func(k int, v string) bool {
if k%2 == 0 {
// halt if we find an even key
return false
}
result = append(result, v)
return true
}
WalkMap(m, f)
must.Eq(t, []string{"one", "three"}, result)
}