Skip to content

Commit 0b95429

Browse files
noahmercadoPaddy
authored andcommitted
Feature: Sum Function (#24666)
The sum function takes a list or set of numbers and returns the sum of those numbers.
1 parent 599cbf8 commit 0b95429

File tree

6 files changed

+260
-0
lines changed

6 files changed

+260
-0
lines changed

lang/funcs/collection.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,53 @@ var MatchkeysFunc = function.New(&function.Spec{
461461
},
462462
})
463463

464+
// SumFunc constructs a function that returns the sum of all
465+
// numbers provided in a list
466+
var SumFunc = function.New(&function.Spec{
467+
Params: []function.Parameter{
468+
{
469+
Name: "list",
470+
Type: cty.DynamicPseudoType,
471+
},
472+
},
473+
Type: function.StaticReturnType(cty.Number),
474+
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
475+
476+
if !args[0].CanIterateElements() {
477+
return cty.NilVal, function.NewArgErrorf(0, "cannot sum noniterable")
478+
}
479+
480+
if args[0].LengthInt() == 0 { // Easy path
481+
return cty.NilVal, function.NewArgErrorf(0, "cannot sum an empty list")
482+
}
483+
484+
arg := args[0].AsValueSlice()
485+
ty := args[0].Type()
486+
487+
var i float64
488+
var s float64
489+
490+
if !ty.IsListType() && !ty.IsSetType() && !ty.IsTupleType() {
491+
return cty.NilVal, function.NewArgErrorf(0, fmt.Sprintf("argument must be list, set, or tuple. Received %s", ty.FriendlyName()))
492+
}
493+
494+
if !args[0].IsKnown() {
495+
return cty.UnknownVal(cty.Number), nil
496+
}
497+
498+
for _, v := range arg {
499+
500+
if err := gocty.FromCtyValue(v, &i); err != nil {
501+
return cty.UnknownVal(cty.Number), function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
502+
} else {
503+
s += i
504+
}
505+
}
506+
507+
return cty.NumberFloatVal(s), nil
508+
},
509+
})
510+
464511
// TransposeFunc constructs a function that takes a map of lists of strings and
465512
// swaps the keys and values to produce a new map of lists of strings.
466513
var TransposeFunc = function.New(&function.Spec{
@@ -570,6 +617,11 @@ func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) {
570617
return MatchkeysFunc.Call([]cty.Value{values, keys, searchset})
571618
}
572619

620+
// Sum adds numbers in a list, set, or tuple
621+
func Sum(list cty.Value) (cty.Value, error) {
622+
return SumFunc.Call([]cty.Value{list})
623+
}
624+
573625
// Transpose takes a map of lists of strings and swaps the keys and values to
574626
// produce a new map of lists of strings.
575627
func Transpose(values cty.Value) (cty.Value, error) {

lang/funcs/collection_test.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,178 @@ func TestMatchkeys(t *testing.T) {
10551055
}
10561056
}
10571057

1058+
func TestSum(t *testing.T) {
1059+
tests := []struct {
1060+
List cty.Value
1061+
Want cty.Value
1062+
Err bool
1063+
}{
1064+
{
1065+
cty.ListVal([]cty.Value{
1066+
cty.NumberIntVal(1),
1067+
cty.NumberIntVal(2),
1068+
cty.NumberIntVal(3),
1069+
}),
1070+
cty.NumberIntVal(6),
1071+
false,
1072+
},
1073+
{
1074+
cty.ListVal([]cty.Value{
1075+
cty.NumberIntVal(1476),
1076+
cty.NumberIntVal(2093),
1077+
cty.NumberIntVal(2092495),
1078+
cty.NumberIntVal(64589234),
1079+
cty.NumberIntVal(234),
1080+
}),
1081+
cty.NumberIntVal(66685532),
1082+
false,
1083+
},
1084+
{
1085+
cty.ListVal([]cty.Value{
1086+
cty.StringVal("a"),
1087+
cty.StringVal("b"),
1088+
cty.StringVal("c"),
1089+
}),
1090+
cty.UnknownVal(cty.String),
1091+
true,
1092+
},
1093+
{
1094+
cty.ListVal([]cty.Value{
1095+
cty.NumberIntVal(10),
1096+
cty.NumberIntVal(-19),
1097+
cty.NumberIntVal(5),
1098+
}),
1099+
cty.NumberIntVal(-4),
1100+
false,
1101+
},
1102+
{
1103+
cty.ListVal([]cty.Value{
1104+
cty.NumberFloatVal(10.2),
1105+
cty.NumberFloatVal(19.4),
1106+
cty.NumberFloatVal(5.7),
1107+
}),
1108+
cty.NumberFloatVal(35.3),
1109+
false,
1110+
},
1111+
{
1112+
cty.ListVal([]cty.Value{
1113+
cty.NumberFloatVal(-10.2),
1114+
cty.NumberFloatVal(-19.4),
1115+
cty.NumberFloatVal(-5.7),
1116+
}),
1117+
cty.NumberFloatVal(-35.3),
1118+
false,
1119+
},
1120+
{
1121+
cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}),
1122+
cty.NilVal,
1123+
true,
1124+
},
1125+
{
1126+
cty.SetVal([]cty.Value{
1127+
cty.StringVal("a"),
1128+
cty.StringVal("b"),
1129+
cty.StringVal("c"),
1130+
}),
1131+
cty.UnknownVal(cty.String),
1132+
true,
1133+
},
1134+
{
1135+
cty.SetVal([]cty.Value{
1136+
cty.NumberIntVal(10),
1137+
cty.NumberIntVal(-19),
1138+
cty.NumberIntVal(5),
1139+
}),
1140+
cty.NumberIntVal(-4),
1141+
false,
1142+
},
1143+
{
1144+
cty.SetVal([]cty.Value{
1145+
cty.NumberIntVal(10),
1146+
cty.NumberIntVal(25),
1147+
cty.NumberIntVal(30),
1148+
}),
1149+
cty.NumberIntVal(65),
1150+
false,
1151+
},
1152+
{
1153+
cty.SetVal([]cty.Value{
1154+
cty.NumberFloatVal(2340.8),
1155+
cty.NumberFloatVal(10.2),
1156+
cty.NumberFloatVal(3),
1157+
}),
1158+
cty.NumberFloatVal(2354),
1159+
false,
1160+
},
1161+
{
1162+
cty.SetVal([]cty.Value{
1163+
cty.NumberFloatVal(2),
1164+
}),
1165+
cty.NumberFloatVal(2),
1166+
false,
1167+
},
1168+
{
1169+
cty.SetVal([]cty.Value{
1170+
cty.NumberFloatVal(-2),
1171+
cty.NumberFloatVal(-50),
1172+
cty.NumberFloatVal(-20),
1173+
cty.NumberFloatVal(-123),
1174+
cty.NumberFloatVal(-4),
1175+
}),
1176+
cty.NumberFloatVal(-199),
1177+
false,
1178+
},
1179+
{
1180+
cty.TupleVal([]cty.Value{
1181+
cty.NumberIntVal(12),
1182+
cty.StringVal("a"),
1183+
cty.NumberIntVal(38),
1184+
}),
1185+
cty.UnknownVal(cty.String),
1186+
true,
1187+
},
1188+
{
1189+
cty.NumberIntVal(12),
1190+
cty.NilVal,
1191+
true,
1192+
},
1193+
{
1194+
cty.ListValEmpty(cty.Number),
1195+
cty.NilVal,
1196+
true,
1197+
},
1198+
{
1199+
cty.MapVal(map[string]cty.Value{"hello": cty.True}),
1200+
cty.NilVal,
1201+
true,
1202+
},
1203+
{
1204+
cty.UnknownVal(cty.Number),
1205+
cty.UnknownVal(cty.Number),
1206+
false,
1207+
},
1208+
}
1209+
1210+
for _, test := range tests {
1211+
t.Run(fmt.Sprintf("sum(%#v)", test.List), func(t *testing.T) {
1212+
got, err := Sum(test.List)
1213+
1214+
if test.Err {
1215+
if err == nil {
1216+
t.Fatal("succeeded; want error")
1217+
}
1218+
return
1219+
} else if err != nil {
1220+
t.Fatalf("unexpected error: %s", err)
1221+
}
1222+
1223+
if !got.RawEquals(test.Want) {
1224+
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
1225+
}
1226+
})
1227+
}
1228+
}
1229+
10581230
func TestTranspose(t *testing.T) {
10591231
tests := []struct {
10601232
Values cty.Value

lang/functions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ func (s *Scope) Functions() map[string]function.Function {
111111
"split": stdlib.SplitFunc,
112112
"strrev": stdlib.ReverseFunc,
113113
"substr": stdlib.SubstrFunc,
114+
"sum": funcs.SumFunc,
114115
"timestamp": funcs.TimestampFunc,
115116
"timeadd": stdlib.TimeAddFunc,
116117
"title": stdlib.TitleFunc,

lang/functions_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,13 @@ func TestFunctions(t *testing.T) {
786786
},
787787
},
788788

789+
"sum": {
790+
{
791+
`sum([2340.5,10,3])`,
792+
cty.NumberFloatVal(2353.5),
793+
},
794+
},
795+
789796
"templatefile": {
790797
{
791798
`templatefile("hello.tmpl", {name = "Jodie"})`,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
layout: "functions"
3+
page_title: "sum - Functions - Configuration Language"
4+
sidebar_current: "docs-funcs-collection-sum"
5+
description: |-
6+
The sum function takes a list or set of numbers and returns the sum of those
7+
numbers.
8+
---
9+
10+
# `sum` Function
11+
12+
-> **Note:** This page is about Terraform 0.13 and later. For Terraform 0.11 and
13+
earlier, see
14+
[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html).
15+
16+
`sum` takes a list or set of numbers and returns the sum of those numbers.
17+
18+
19+
## Examples
20+
21+
```
22+
> sum([10, 13, 6, 4.5])
23+
33.5
24+
```

website/layouts/functions.erb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,10 @@
238238
<a href="/docs/configuration/functions/sort.html">sort</a>
239239
</li>
240240

241+
<li>
242+
<a href="/docs/configuration/functions/sum.html">sum</a>
243+
</li>
244+
241245
<li>
242246
<a href="/docs/configuration/functions/transpose.html">transpose</a>
243247
</li>

0 commit comments

Comments
 (0)