From ddb5885a7252d3ede98d711b0e379bda0807a3c5 Mon Sep 17 00:00:00 2001 From: bejaratommy Date: Sat, 4 Jul 2026 10:09:05 +0000 Subject: [PATCH] Support any map type in the keys function The keys function was typed as keys(...map[string]interface{}), so Go's template engine could only pass it dicts of that exact type. Calling keys on any other map (e.g. map[string]struct{...} or map[string]int) failed with a type mismatch. Accept ...interface{} and collect keys reflectively, stringifying each key. Existing dict (map[string]interface{}) usage is unchanged. Fixes #455 --- dict.go | 13 ++++++++++--- dict_test.go | 18 ++++++++++++++++++ docs/dicts.md | 3 ++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/dict.go b/dict.go index 4315b354..41913dc7 100644 --- a/dict.go +++ b/dict.go @@ -1,6 +1,9 @@ package sprig import ( + "fmt" + "reflect" + "dario.cat/mergo" "github.com/mitchellh/copystructure" ) @@ -37,11 +40,15 @@ func pluck(key string, d ...map[string]interface{}) []interface{} { return res } -func keys(dicts ...map[string]interface{}) []string { +func keys(dicts ...interface{}) []string { k := []string{} for _, dict := range dicts { - for key := range dict { - k = append(k, key) + m := reflect.ValueOf(dict) + if m.Kind() != reflect.Map { + continue + } + for _, key := range m.MapKeys() { + k = append(k, fmt.Sprint(key.Interface())) } } return k diff --git a/dict_test.go b/dict_test.go index c829daa5..d5952a05 100644 --- a/dict_test.go +++ b/dict_test.go @@ -82,6 +82,24 @@ func TestKeys(t *testing.T) { } } +func TestKeysWithTypedMaps(t *testing.T) { + // keys should work with maps whose value type is not interface{} (issue #455). + type item struct{ N int } + vars := map[string]interface{}{ + "structMap": map[string]item{"foo": {1}, "bar": {2}}, + "intMap": map[string]int{"a": 1, "b": 2}, + } + tests := map[string]string{ + `{{ keys .structMap | sortAlpha }}`: "[bar foo]", + `{{ keys .intMap | sortAlpha }}`: "[a b]", + } + for tpl, expect := range tests { + if err := runtv(tpl, expect, vars); err != nil { + t.Error(err) + } + } +} + func TestPick(t *testing.T) { tests := map[string]string{ `{{- $d := dict "one" 1 "two" 222222 }}{{ pick $d "two" | len -}}`: "1", diff --git a/docs/dicts.md b/docs/dicts.md index ef441a1b..06dc95ea 100644 --- a/docs/dicts.md +++ b/docs/dicts.md @@ -184,7 +184,8 @@ deepCopy $source | mergeOverwrite $dest ## keys The `keys` function will return a `list` of all of the keys in one or more `dict` -types. Since a dictionary is _unordered_, the keys will not be in a predictable order. +types. Any map type is accepted, not only `dict` (`map[string]interface{}`). +Since a dictionary is _unordered_, the keys will not be in a predictable order. They can be sorted with `sortAlpha`. ```