diff --git a/README.md b/README.md index b0be7393..8be37e11 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat "one anchovy" "many anchovies"` - uuidv4: Generate a UUID v4 string - sha256sum: Generate a hex encoded sha256 hash of the input +- toString: Convert something to a string ### String Slice Functions: @@ -117,6 +118,8 @@ parse, it returns the time unaltered. See `time.ParseDuration` for info on durat Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`) - splitList: strings.Split, but as `split SEP STRING`. The results are returned as an array. +- toStrings: convert a list to a list of strings. 'list 1 2 3 | toStrings' produces '["1" "2" "3"]' +- sortAlpha: sort a list lexicographically. ### Integer Slice Functions: diff --git a/functions.go b/functions.go index 95a74ffa..bfb9966f 100644 --- a/functions.go +++ b/functions.go @@ -60,6 +60,7 @@ String Functions - replace: Replace an old with a new in a string: `$name | replace " " "-"` - plural: Choose singular or plural based on length: `len $fish | plural "one anchovy" "many anchovies"` - sha256sum: Generate a hex encoded sha256 hash of the input + - toString: Convert something to a string String Slice Functions: @@ -69,6 +70,8 @@ String Slice Functions: Use it like this: `{{$v := "foo/bar/baz" | split "/"}}{{$v._0}}` (Prints `foo`) - splitList: strings.Split, but as `split SEP STRING`. The results are returned as an array. + - toStrings: convert a list to a list of strings. 'list 1 2 3 | toStrings' produces '["1" "2" "3"]' + - sortAlpha: sort a list lexicographically. Integer Slice Functions: @@ -211,6 +214,7 @@ import ( "os" "path" "reflect" + "sort" "strconv" "strings" ttemplate "text/template" @@ -336,6 +340,7 @@ var genericMap = map[string]interface{}{ "replace": replace, "plural": plural, "sha256sum": sha256sum, + "toString": strval, // Wrap Atoi to stop errors. "atoi": func(a string) int { i, _ := strconv.Atoi(a); return i }, @@ -351,6 +356,7 @@ var genericMap = map[string]interface{}{ // split "/" foo/bar returns map[int]string{0: foo, 1: bar} "split": split, "splitList": func(sep, orig string) []string { return strings.Split(orig, sep) }, + "toStrings": strslice, "until": until, "untilStep": untilStep, @@ -380,7 +386,8 @@ var genericMap = map[string]interface{}{ // string slices. Note that we reverse the order b/c that's better // for template processing. - "join": join, + "join": join, + "sortAlpha": sortAlpha, // Defaults "default": dfault, @@ -754,17 +761,27 @@ func dict(v ...interface{}) map[string]interface{} { } func join(sep string, v interface{}) string { - // The first two cases bypass the overhead of the reflection system. + return strings.Join(strslice(v), sep) +} + +func sortAlpha(array []interface{}) []string { + a := strslice(array) + s := sort.StringSlice(a) + s.Sort() + return s +} + +func strslice(v interface{}) []string { switch v := v.(type) { case []string: - return strings.Join(v, sep) + return v case []interface{}: l := len(v) b := make([]string, l) for i := 0; i < l; i++ { b[i] = strval(v[i]) } - return strings.Join(b, sep) + return b default: val := reflect.ValueOf(v) switch val.Kind() { @@ -774,9 +791,9 @@ func join(sep string, v interface{}) string { for i := 0; i < l; i++ { b[i] = strval(val.Index(i).Interface()) } - return strings.Join(b, sep) + return b default: - return strval(v) + return []string{strval(v)} } } } diff --git a/functions_test.go b/functions_test.go index a08fc0fb..019f1975 100644 --- a/functions_test.go +++ b/functions_test.go @@ -304,6 +304,16 @@ func TestSplit(t *testing.T) { } } +func TestToString(t *testing.T) { + tpl := `{{ toString 1 | kindOf }}` + assert.NoError(t, runt(tpl, "string")) +} + +func TestToStrings(t *testing.T) { + tpl := `{{ $s := list 1 2 3 | toStrings }}{{ index $s 1 | kindOf }}` + assert.NoError(t, runt(tpl, "string")) +} + type fixtureTO struct { Name, Value string } @@ -698,6 +708,17 @@ func TestJoin(t *testing.T) { assert.NoError(t, runtv(`{{ join "-" .V }}`, "1-2-3", map[string]interface{}{"V": []int{1, 2, 3}})) } +func TestSortAlpha(t *testing.T) { + // Named `append` in the function map + tests := map[string]string{ + `{{ list "c" "a" "b" | sortAlpha | join "" }}`: "abc", + `{{ list 2 1 4 3 | sortAlpha | join "" }}`: "1234", + } + for tpl, expect := range tests { + assert.NoError(t, runt(tpl, expect)) + } +} + func TestDelete(t *testing.T) { fmap := TxtFuncMap() delete(fmap, "split")