forked from p30928647/excelize
ref #65, new formula functions: AVERAGEIFS and SUMPRODUCT
This commit is contained in:
parent
797958210d
commit
139ee4c4b0
100
calc.go
100
calc.go
|
@ -328,6 +328,7 @@ type formulaFuncs struct {
|
|||
// AVERAGE
|
||||
// AVERAGEA
|
||||
// AVERAGEIF
|
||||
// AVERAGEIFS
|
||||
// BASE
|
||||
// BESSELI
|
||||
// BESSELJ
|
||||
|
@ -626,6 +627,7 @@ type formulaFuncs struct {
|
|||
// SUM
|
||||
// SUMIF
|
||||
// SUMIFS
|
||||
// SUMPRODUCT
|
||||
// SUMSQ
|
||||
// SUMX2MY2
|
||||
// SUMX2PY2
|
||||
|
@ -4995,6 +4997,73 @@ func (fn *formulaFuncs) SUMIFS(argsList *list.List) formulaArg {
|
|||
return newNumberFormulaArg(sum)
|
||||
}
|
||||
|
||||
// sumproduct is an implementation of the formula function SUMPRODUCT.
|
||||
func (fn *formulaFuncs) sumproduct(argsList *list.List) formulaArg {
|
||||
var (
|
||||
argType ArgType
|
||||
n int
|
||||
res []float64
|
||||
sum float64
|
||||
)
|
||||
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
|
||||
token := arg.Value.(formulaArg)
|
||||
if argType == ArgUnknown {
|
||||
argType = token.Type
|
||||
}
|
||||
if token.Type != argType {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||
}
|
||||
switch token.Type {
|
||||
case ArgString, ArgNumber:
|
||||
if num := token.ToNumber(); num.Type == ArgNumber {
|
||||
sum = fn.PRODUCT(argsList).Number
|
||||
continue
|
||||
}
|
||||
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||
case ArgMatrix:
|
||||
args := token.ToList()
|
||||
if res == nil {
|
||||
n = len(args)
|
||||
res = make([]float64, n)
|
||||
for i := range res {
|
||||
res[i] = 1.0
|
||||
}
|
||||
}
|
||||
if len(args) != n {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||
}
|
||||
for i, value := range args {
|
||||
num := value.ToNumber()
|
||||
if num.Type != ArgNumber && value.Value() != "" {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
||||
}
|
||||
res[i] = res[i] * num.Number
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range res {
|
||||
sum += r
|
||||
}
|
||||
return newNumberFormulaArg(sum)
|
||||
}
|
||||
|
||||
// SUMPRODUCT function returns the sum of the products of the corresponding
|
||||
// values in a set of supplied arrays. The syntax of the function is:
|
||||
//
|
||||
// SUMPRODUCT(array1,[array2],[array3],...)
|
||||
//
|
||||
func (fn *formulaFuncs) SUMPRODUCT(argsList *list.List) formulaArg {
|
||||
if argsList.Len() < 1 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "SUMPRODUCT requires at least 1 argument")
|
||||
}
|
||||
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
|
||||
if token := arg.Value.(formulaArg); token.Type == ArgError {
|
||||
return token
|
||||
}
|
||||
}
|
||||
return fn.sumproduct(argsList)
|
||||
}
|
||||
|
||||
// SUMSQ function returns the sum of squares of a supplied set of values. The
|
||||
// syntax of the function is:
|
||||
//
|
||||
|
@ -5270,6 +5339,37 @@ func (fn *formulaFuncs) AVERAGEIF(argsList *list.List) formulaArg {
|
|||
return newNumberFormulaArg(sum / count)
|
||||
}
|
||||
|
||||
// AVERAGEIFS function finds entries in one or more arrays, that satisfy a set
|
||||
// of supplied criteria, and returns the average (i.e. the statistical mean)
|
||||
// of the corresponding values in a further supplied array. The syntax of the
|
||||
// function is:
|
||||
//
|
||||
// AVERAGEIFS(average_range,criteria_range1,criteria1,[criteria_range2,criteria2],...)
|
||||
//
|
||||
func (fn *formulaFuncs) AVERAGEIFS(argsList *list.List) formulaArg {
|
||||
if argsList.Len() < 3 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "AVERAGEIFS requires at least 3 arguments")
|
||||
}
|
||||
if argsList.Len()%2 != 1 {
|
||||
return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
|
||||
}
|
||||
sum, sumRange, args := 0.0, argsList.Front().Value.(formulaArg).Matrix, []formulaArg{}
|
||||
for arg := argsList.Front().Next(); arg != nil; arg = arg.Next() {
|
||||
args = append(args, arg.Value.(formulaArg))
|
||||
}
|
||||
count := 0.0
|
||||
for _, ref := range formulaIfsMatch(args) {
|
||||
if num := sumRange[ref.Row][ref.Col].ToNumber(); num.Type == ArgNumber {
|
||||
sum += num.Number
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
return newErrorFormulaArg(formulaErrorDIV, "AVERAGEIF divide by zero")
|
||||
}
|
||||
return newNumberFormulaArg(sum / count)
|
||||
}
|
||||
|
||||
// getBetaHelperContFrac continued fractions for the beta function.
|
||||
func getBetaHelperContFrac(fX, fA, fB float64) float64 {
|
||||
var a1, b1, a2, b2, fnorm, cfnew, cf, rm float64
|
||||
|
|
25
calc_test.go
25
calc_test.go
|
@ -741,6 +741,12 @@ func TestCalcCellValue(t *testing.T) {
|
|||
`=SUMIF(E2:E9,"North 1",F2:F9)`: "66582",
|
||||
`=SUMIF(E2:E9,"North*",F2:F9)`: "138772",
|
||||
"=SUMIF(D1:D3,\"Month\",D1:D3)": "0",
|
||||
// SUMPRODUCT
|
||||
"=SUMPRODUCT(A1,B1)": "4",
|
||||
"=SUMPRODUCT(A1:A2,B1:B2)": "14",
|
||||
"=SUMPRODUCT(A1:A3,B1:B3)": "14",
|
||||
"=SUMPRODUCT(A1:B3)": "15",
|
||||
"=SUMPRODUCT(A1:A3,B1:B3,B2:B4)": "20",
|
||||
// SUMSQ
|
||||
"=SUMSQ(A1:A4)": "14",
|
||||
"=SUMSQ(A1,B1,A2,B2,6)": "82",
|
||||
|
@ -2351,6 +2357,13 @@ func TestCalcCellValue(t *testing.T) {
|
|||
// SUMSQ
|
||||
`=SUMSQ("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
|
||||
"=SUMSQ(C1:D2)": "strconv.ParseFloat: parsing \"Month\": invalid syntax",
|
||||
// SUMPRODUCT
|
||||
"=SUMPRODUCT()": "SUMPRODUCT requires at least 1 argument",
|
||||
"=SUMPRODUCT(A1,B1:B2)": "#VALUE!",
|
||||
"=SUMPRODUCT(A1,D1)": "#VALUE!",
|
||||
"=SUMPRODUCT(A1:A3,D1:D3)": "#VALUE!",
|
||||
"=SUMPRODUCT(A1:A2,B1:B3)": "#VALUE!",
|
||||
"=SUMPRODUCT(A1,NA())": "#N/A",
|
||||
// SUMX2MY2
|
||||
"=SUMX2MY2()": "SUMX2MY2 requires 2 arguments",
|
||||
"=SUMX2MY2(A1,B1:B2)": "#N/A",
|
||||
|
@ -4231,7 +4244,7 @@ func TestCalcMIRR(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCalcSUMIFS(t *testing.T) {
|
||||
func TestCalcSUMIFSAndAVERAGEIFS(t *testing.T) {
|
||||
cellData := [][]interface{}{
|
||||
{"Quarter", "Area", "Sales Rep.", "Sales"},
|
||||
{1, "North", "Jeff", 223000},
|
||||
|
@ -4249,8 +4262,10 @@ func TestCalcSUMIFS(t *testing.T) {
|
|||
}
|
||||
f := prepareCalcData(cellData)
|
||||
formulaList := map[string]string{
|
||||
"=SUMIFS(D2:D13,A2:A13,1,B2:B13,\"North\")": "348000",
|
||||
"=SUMIFS(D2:D13,A2:A13,\">2\",C2:C13,\"Jeff\")": "571000",
|
||||
"=AVERAGEIFS(D2:D13,A2:A13,1,B2:B13,\"North\")": "174000",
|
||||
"=AVERAGEIFS(D2:D13,A2:A13,\">2\",C2:C13,\"Jeff\")": "285500",
|
||||
"=SUMIFS(D2:D13,A2:A13,1,B2:B13,\"North\")": "348000",
|
||||
"=SUMIFS(D2:D13,A2:A13,\">2\",C2:C13,\"Jeff\")": "571000",
|
||||
}
|
||||
for formula, expected := range formulaList {
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "E1", formula))
|
||||
|
@ -4259,6 +4274,10 @@ func TestCalcSUMIFS(t *testing.T) {
|
|||
assert.Equal(t, expected, result, formula)
|
||||
}
|
||||
calcError := map[string]string{
|
||||
"=AVERAGEIFS()": "AVERAGEIFS requires at least 3 arguments",
|
||||
"=AVERAGEIFS(H1,\"\")": "AVERAGEIFS requires at least 3 arguments",
|
||||
"=AVERAGEIFS(H1,\"\",TRUE,1)": "#N/A",
|
||||
"=AVERAGEIFS(H1,\"\",TRUE)": "AVERAGEIF divide by zero",
|
||||
"=SUMIFS()": "SUMIFS requires at least 3 arguments",
|
||||
"=SUMIFS(D2:D13,A2:A13,1,B2:B13)": "#N/A",
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue