mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Feature: Sum Function (#24666)
The sum function takes a list or set of numbers and returns the sum of those numbers.
This commit is contained in:
parent
8d71337596
commit
d4d8812afa
@ -461,6 +461,53 @@ var MatchkeysFunc = function.New(&function.Spec{
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// SumFunc constructs a function that returns the sum of all
|
||||||
|
// numbers provided in a list
|
||||||
|
var SumFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "list",
|
||||||
|
Type: cty.DynamicPseudoType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.Number),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
|
||||||
|
if !args[0].CanIterateElements() {
|
||||||
|
return cty.NilVal, function.NewArgErrorf(0, "cannot sum noniterable")
|
||||||
|
}
|
||||||
|
|
||||||
|
if args[0].LengthInt() == 0 { // Easy path
|
||||||
|
return cty.NilVal, function.NewArgErrorf(0, "cannot sum an empty list")
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := args[0].AsValueSlice()
|
||||||
|
ty := args[0].Type()
|
||||||
|
|
||||||
|
var i float64
|
||||||
|
var s float64
|
||||||
|
|
||||||
|
if !ty.IsListType() && !ty.IsSetType() && !ty.IsTupleType() {
|
||||||
|
return cty.NilVal, function.NewArgErrorf(0, fmt.Sprintf("argument must be list, set, or tuple. Received %s", ty.FriendlyName()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !args[0].IsKnown() {
|
||||||
|
return cty.UnknownVal(cty.Number), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range arg {
|
||||||
|
|
||||||
|
if err := gocty.FromCtyValue(v, &i); err != nil {
|
||||||
|
return cty.UnknownVal(cty.Number), function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
|
||||||
|
} else {
|
||||||
|
s += i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.NumberFloatVal(s), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// TransposeFunc constructs a function that takes a map of lists of strings and
|
// TransposeFunc constructs a function that takes a map of lists of strings and
|
||||||
// swaps the keys and values to produce a new map of lists of strings.
|
// swaps the keys and values to produce a new map of lists of strings.
|
||||||
var TransposeFunc = function.New(&function.Spec{
|
var TransposeFunc = function.New(&function.Spec{
|
||||||
@ -570,6 +617,11 @@ func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) {
|
|||||||
return MatchkeysFunc.Call([]cty.Value{values, keys, searchset})
|
return MatchkeysFunc.Call([]cty.Value{values, keys, searchset})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sum adds numbers in a list, set, or tuple
|
||||||
|
func Sum(list cty.Value) (cty.Value, error) {
|
||||||
|
return SumFunc.Call([]cty.Value{list})
|
||||||
|
}
|
||||||
|
|
||||||
// Transpose takes a map of lists of strings and swaps the keys and values to
|
// Transpose takes a map of lists of strings and swaps the keys and values to
|
||||||
// produce a new map of lists of strings.
|
// produce a new map of lists of strings.
|
||||||
func Transpose(values cty.Value) (cty.Value, error) {
|
func Transpose(values cty.Value) (cty.Value, error) {
|
||||||
|
@ -1055,6 +1055,178 @@ func TestMatchkeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
List cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.NumberIntVal(1),
|
||||||
|
cty.NumberIntVal(2),
|
||||||
|
cty.NumberIntVal(3),
|
||||||
|
}),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.NumberIntVal(1476),
|
||||||
|
cty.NumberIntVal(2093),
|
||||||
|
cty.NumberIntVal(2092495),
|
||||||
|
cty.NumberIntVal(64589234),
|
||||||
|
cty.NumberIntVal(234),
|
||||||
|
}),
|
||||||
|
cty.NumberIntVal(66685532),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("a"),
|
||||||
|
cty.StringVal("b"),
|
||||||
|
cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.NumberIntVal(10),
|
||||||
|
cty.NumberIntVal(-19),
|
||||||
|
cty.NumberIntVal(5),
|
||||||
|
}),
|
||||||
|
cty.NumberIntVal(-4),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.NumberFloatVal(10.2),
|
||||||
|
cty.NumberFloatVal(19.4),
|
||||||
|
cty.NumberFloatVal(5.7),
|
||||||
|
}),
|
||||||
|
cty.NumberFloatVal(35.3),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.NumberFloatVal(-10.2),
|
||||||
|
cty.NumberFloatVal(-19.4),
|
||||||
|
cty.NumberFloatVal(-5.7),
|
||||||
|
}),
|
||||||
|
cty.NumberFloatVal(-35.3),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}),
|
||||||
|
cty.NilVal,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.StringVal("a"),
|
||||||
|
cty.StringVal("b"),
|
||||||
|
cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.NumberIntVal(10),
|
||||||
|
cty.NumberIntVal(-19),
|
||||||
|
cty.NumberIntVal(5),
|
||||||
|
}),
|
||||||
|
cty.NumberIntVal(-4),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.NumberIntVal(10),
|
||||||
|
cty.NumberIntVal(25),
|
||||||
|
cty.NumberIntVal(30),
|
||||||
|
}),
|
||||||
|
cty.NumberIntVal(65),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.NumberFloatVal(2340.8),
|
||||||
|
cty.NumberFloatVal(10.2),
|
||||||
|
cty.NumberFloatVal(3),
|
||||||
|
}),
|
||||||
|
cty.NumberFloatVal(2354),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.NumberFloatVal(2),
|
||||||
|
}),
|
||||||
|
cty.NumberFloatVal(2),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.SetVal([]cty.Value{
|
||||||
|
cty.NumberFloatVal(-2),
|
||||||
|
cty.NumberFloatVal(-50),
|
||||||
|
cty.NumberFloatVal(-20),
|
||||||
|
cty.NumberFloatVal(-123),
|
||||||
|
cty.NumberFloatVal(-4),
|
||||||
|
}),
|
||||||
|
cty.NumberFloatVal(-199),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.TupleVal([]cty.Value{
|
||||||
|
cty.NumberIntVal(12),
|
||||||
|
cty.StringVal("a"),
|
||||||
|
cty.NumberIntVal(38),
|
||||||
|
}),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.NumberIntVal(12),
|
||||||
|
cty.NilVal,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.ListValEmpty(cty.Number),
|
||||||
|
cty.NilVal,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.MapVal(map[string]cty.Value{"hello": cty.True}),
|
||||||
|
cty.NilVal,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.UnknownVal(cty.Number),
|
||||||
|
cty.UnknownVal(cty.Number),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("sum(%#v)", test.List), func(t *testing.T) {
|
||||||
|
got, err := Sum(test.List)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTranspose(t *testing.T) {
|
func TestTranspose(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Values cty.Value
|
Values cty.Value
|
||||||
|
@ -111,6 +111,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||||||
"split": stdlib.SplitFunc,
|
"split": stdlib.SplitFunc,
|
||||||
"strrev": stdlib.ReverseFunc,
|
"strrev": stdlib.ReverseFunc,
|
||||||
"substr": stdlib.SubstrFunc,
|
"substr": stdlib.SubstrFunc,
|
||||||
|
"sum": funcs.SumFunc,
|
||||||
"timestamp": funcs.TimestampFunc,
|
"timestamp": funcs.TimestampFunc,
|
||||||
"timeadd": stdlib.TimeAddFunc,
|
"timeadd": stdlib.TimeAddFunc,
|
||||||
"title": stdlib.TitleFunc,
|
"title": stdlib.TitleFunc,
|
||||||
|
@ -786,6 +786,13 @@ func TestFunctions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"sum": {
|
||||||
|
{
|
||||||
|
`sum([2340.5,10,3])`,
|
||||||
|
cty.NumberFloatVal(2353.5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"templatefile": {
|
"templatefile": {
|
||||||
{
|
{
|
||||||
`templatefile("hello.tmpl", {name = "Jodie"})`,
|
`templatefile("hello.tmpl", {name = "Jodie"})`,
|
||||||
|
24
website/docs/configuration/functions/sum.html.md
Normal file
24
website/docs/configuration/functions/sum.html.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
layout: "functions"
|
||||||
|
page_title: "sum - Functions - Configuration Language"
|
||||||
|
sidebar_current: "docs-funcs-collection-sum"
|
||||||
|
description: |-
|
||||||
|
The sum function takes a list or set of numbers and returns the sum of those
|
||||||
|
numbers.
|
||||||
|
---
|
||||||
|
|
||||||
|
# `sum` Function
|
||||||
|
|
||||||
|
-> **Note:** This page is about Terraform 0.13 and later. For Terraform 0.11 and
|
||||||
|
earlier, see
|
||||||
|
[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html).
|
||||||
|
|
||||||
|
`sum` takes a list or set of numbers and returns the sum of those numbers.
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```
|
||||||
|
> sum([10, 13, 6, 4.5])
|
||||||
|
33.5
|
||||||
|
```
|
@ -238,6 +238,10 @@
|
|||||||
<a href="/docs/configuration/functions/sort.html">sort</a>
|
<a href="/docs/configuration/functions/sort.html">sort</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href="/docs/configuration/functions/sum.html">sum</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="/docs/configuration/functions/transpose.html">transpose</a>
|
<a href="/docs/configuration/functions/transpose.html">transpose</a>
|
||||||
</li>
|
</li>
|
||||||
|
Loading…
Reference in New Issue
Block a user