diff --git a/lib/lang/maps.go b/lib/lang/maps.go new file mode 100644 index 000000000..17b62323f --- /dev/null +++ b/lib/lang/maps.go @@ -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 + } + } +} diff --git a/lib/lang/maps_test.go b/lib/lang/maps_test.go new file mode 100644 index 000000000..8ea3034a6 --- /dev/null +++ b/lib/lang/maps_test.go @@ -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) +}