config: new "transpose" interpolation function

This function takes a map of lists of strings and inverts it so that
the string values become keys and the keys become items within the
corresponding lists.
This commit is contained in:
Ariel Alonso 2017-09-02 00:19:47 -04:00 committed by Martin Atkins
parent a28b5d295e
commit 550ae05819
3 changed files with 126 additions and 0 deletions

View File

@ -112,6 +112,7 @@ func Funcs() map[string]ast.Function {
"substr": interpolationFuncSubstr(),
"timestamp": interpolationFuncTimestamp(),
"title": interpolationFuncTitle(),
"transpose": interpolationFuncTranspose(),
"trimspace": interpolationFuncTrimSpace(),
"upper": interpolationFuncUpper(),
"urlencode": interpolationFuncURLEncode(),
@ -1548,6 +1549,53 @@ func interpolationFuncURLEncode() ast.Function {
}
}
// interpolationFuncTranspose implements the "transpose" function
// that converts a map (string,list) to a map (string,list) where
// the unique values of the original lists become the keys of the
// new map and the keys of the original map become values for the
// corresponding new keys.
func interpolationFuncTranspose() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeMap},
ReturnType: ast.TypeMap,
Callback: func(args []interface{}) (interface{}, error) {
inputMap := args[0].(map[string]ast.Variable)
outputMap := make(map[string]ast.Variable)
tmpMap := make(map[string][]string)
for inKey, inVal := range inputMap {
if inVal.Type != ast.TypeList {
return nil, fmt.Errorf("transpose requires a map of lists of strings")
}
values := inVal.Value.([]ast.Variable)
for _, listVal := range values {
if listVal.Type != ast.TypeString {
return nil, fmt.Errorf("transpose requires the given map values to be lists of strings")
}
outKey := listVal.Value.(string)
if _, ok := tmpMap[outKey]; !ok {
tmpMap[outKey] = make([]string, 0)
}
outVal := tmpMap[outKey]
outVal = append(outVal, inKey)
sort.Strings(outVal)
tmpMap[outKey] = outVal
}
}
for outKey, outVal := range tmpMap {
values := make([]ast.Variable, 0)
for _, v := range outVal {
values = append(values, ast.Variable{Type: ast.TypeString, Value: v})
}
outputMap[outKey] = ast.Variable{Type: ast.TypeList, Value: values}
}
return outputMap, nil
},
}
}
// interpolationFuncAbs returns the absolute value of a given float.
func interpolationFuncAbs() ast.Function {
return ast.Function{

View File

@ -2615,6 +2615,82 @@ func TestInterpolateFuncURLEncode(t *testing.T) {
})
}
func TestInterpolateFuncTranspose(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]ast.Variable{
"var.map": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "a"},
{Type: ast.TypeString, Value: "b"},
},
},
"key2": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "a"},
{Type: ast.TypeString, Value: "b"},
{Type: ast.TypeString, Value: "c"},
},
},
"key3": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeString, Value: "c"},
},
},
"key4": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{},
},
}},
"var.badmap": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": ast.Variable{
Type: ast.TypeList,
Value: []ast.Variable{
{Type: ast.TypeList, Value: []ast.Variable{}},
{Type: ast.TypeList, Value: []ast.Variable{}},
},
},
}},
"var.worsemap": ast.Variable{
Type: ast.TypeMap,
Value: map[string]ast.Variable{
"key1": ast.Variable{
Type: ast.TypeString,
Value: "not-a-list",
},
}},
},
Cases: []testFunctionCase{
{
`${transpose(var.map)}`,
map[string]interface{}{
"a": []interface{}{"key1", "key2"},
"b": []interface{}{"key1", "key2"},
"c": []interface{}{"key2", "key3"},
},
false,
},
{
`${transpose(var.badmap)}`,
nil,
true,
},
{
`${transpose(var.worsemap)}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncAbs(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{

View File

@ -392,6 +392,8 @@ The supported built-in functions are:
* `title(string)` - Returns a copy of the string with the first characters of all the words capitalized.
* `transpose(map)` - Swaps the keys and list values in a map of lists of strings. For example, transpose(map("a", list("1", "2"), "b", list("2", "3")) produces a value equivalent to map("1", list("a"), "2", list("a", "b"), "3", list("b")).
* `trimspace(string)` - Returns a copy of the string with all leading and trailing white spaces removed.
* `upper(string)` - Returns a copy of the string with all Unicode letters mapped to their upper case.