diff options
author | xuri <xuri.me@gmail.com> | 2021-12-13 00:02:25 +0800 |
---|---|---|
committer | xuri <xuri.me@gmail.com> | 2021-12-13 00:02:25 +0800 |
commit | 33719334945f0ce0752cedfbd4267b83850ab85d (patch) | |
tree | 2d1e45b355a36cdd54c789e5182e77ae02b8cbf5 /calc.go | |
parent | 76aacfda0b7c0b3b21c2e0909b0f24eb9c5769a4 (diff) |
ref #65: new formula functions: AVERAGEIF and COUNTIF
Diffstat (limited to 'calc.go')
-rw-r--r-- | calc.go | 110 |
1 files changed, 87 insertions, 23 deletions
@@ -52,6 +52,7 @@ const ( criteriaEq criteriaLe criteriaGe + criteriaNe criteriaL criteriaG criteriaErr @@ -315,6 +316,7 @@ type formulaFuncs struct { // AVEDEV // AVERAGE // AVERAGEA +// AVERAGEIF // BASE // BESSELI // BESSELJ @@ -352,6 +354,7 @@ type formulaFuncs struct { // COUNT // COUNTA // COUNTBLANK +// COUNTIF // COUPDAYBS // COUPDAYS // COUPDAYSNC @@ -1419,6 +1422,10 @@ func formulaCriteriaParser(exp string) (fc *formulaCriteria) { fc.Type, fc.Condition = criteriaEq, match[1] return } + if match := regexp.MustCompile(`^<>(.*)$`).FindStringSubmatch(exp); len(match) > 1 { + fc.Type, fc.Condition = criteriaNe, match[1] + return + } if match := regexp.MustCompile(`^<=(.*)$`).FindStringSubmatch(exp); len(match) > 1 { fc.Type, fc.Condition = criteriaLe, match[1] return @@ -1467,6 +1474,8 @@ func formulaCriteriaEval(val string, criteria *formulaCriteria) (result bool, er case criteriaGe: value, expected, e = prepareValue(val, criteria.Condition) return value >= expected && e == nil, err + case criteriaNe: + return val != criteria.Condition, err case criteriaL: value, expected, e = prepareValue(val, criteria.Condition) return value < expected && e == nil, err @@ -4723,7 +4732,7 @@ func (fn *formulaFuncs) SUM(argsList *list.List) formulaArg { // func (fn *formulaFuncs) SUMIF(argsList *list.List) formulaArg { if argsList.Len() < 2 { - return newErrorFormulaArg(formulaErrorVALUE, "SUMIF requires at least 2 argument") + return newErrorFormulaArg(formulaErrorVALUE, "SUMIF requires at least 2 arguments") } var criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) var rangeMtx = argsList.Front().Value.(formulaArg).Matrix @@ -4740,9 +4749,7 @@ func (fn *formulaFuncs) SUMIF(argsList *list.List) formulaArg { if col.String == "" { continue } - if ok, err = formulaCriteriaEval(fromVal, criteria); err != nil { - return newErrorFormulaArg(formulaErrorVALUE, err.Error()) - } + ok, _ = formulaCriteriaEval(fromVal, criteria) if ok { if argsList.Len() == 3 { if len(sumRange) <= rowIdx || len(sumRange[rowIdx]) <= colIdx { @@ -4751,7 +4758,7 @@ func (fn *formulaFuncs) SUMIF(argsList *list.List) formulaArg { fromVal = sumRange[rowIdx][colIdx].String } if val, err = strconv.ParseFloat(fromVal, 64); err != nil { - return newErrorFormulaArg(formulaErrorVALUE, err.Error()) + continue } sum += val } @@ -4927,6 +4934,57 @@ func (fn *formulaFuncs) AVERAGEA(argsList *list.List) formulaArg { return newNumberFormulaArg(sum / count) } +// AVERAGEIF function finds the values in a supplied array that satisfy a +// specified criteria, and returns the average (i.e. the statistical mean) of +// the corresponding values in a second supplied array. The syntax of the +// function is: +// +// AVERAGEIF(range,criteria,[average_range]) +// +func (fn *formulaFuncs) AVERAGEIF(argsList *list.List) formulaArg { + if argsList.Len() < 2 { + return newErrorFormulaArg(formulaErrorVALUE, "AVERAGEIF requires at least 2 arguments") + } + var ( + criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) + rangeMtx = argsList.Front().Value.(formulaArg).Matrix + cellRange [][]formulaArg + args []formulaArg + val float64 + err error + ok bool + ) + if argsList.Len() == 3 { + cellRange = argsList.Back().Value.(formulaArg).Matrix + } + for rowIdx, row := range rangeMtx { + for colIdx, col := range row { + fromVal := col.String + if col.String == "" { + continue + } + ok, _ = formulaCriteriaEval(fromVal, criteria) + if ok { + if argsList.Len() == 3 { + if len(cellRange) <= rowIdx || len(cellRange[rowIdx]) <= colIdx { + continue + } + fromVal = cellRange[rowIdx][colIdx].String + } + if val, err = strconv.ParseFloat(fromVal, 64); err != nil { + continue + } + args = append(args, newNumberFormulaArg(val)) + } + } + } + count, sum := fn.countSum(false, args) + if count == 0 { + return newErrorFormulaArg(formulaErrorDIV, "AVERAGEIF divide by zero") + } + return newNumberFormulaArg(sum / count) +} + // incompleteGamma is an implementation of the incomplete gamma function. func incompleteGamma(a, x float64) float64 { max := 32 @@ -5134,28 +5192,34 @@ func (fn *formulaFuncs) COUNTBLANK(argsList *list.List) formulaArg { if argsList.Len() != 1 { return newErrorFormulaArg(formulaErrorVALUE, "COUNTBLANK requires 1 argument") } - var count int - token := argsList.Front().Value.(formulaArg) - switch token.Type { - case ArgString: - if token.String == "" { + var count float64 + for _, cell := range argsList.Front().Value.(formulaArg).ToList() { + if cell.Value() == "" { count++ } - case ArgList, ArgMatrix: - for _, row := range token.ToList() { - switch row.Type { - case ArgString: - if row.String == "" { - count++ - } - case ArgEmpty: - count++ - } + } + return newNumberFormulaArg(count) +} + +// COUNTIF function returns the number of cells within a supplied range, that +// satisfy a given criteria. The syntax of the function is: +// +// COUNTIF(range,criteria) +// +func (fn *formulaFuncs) COUNTIF(argsList *list.List) formulaArg { + if argsList.Len() != 2 { + return newErrorFormulaArg(formulaErrorVALUE, "COUNTIF requires 2 arguments") + } + var ( + criteria = formulaCriteriaParser(argsList.Front().Next().Value.(formulaArg).String) + count float64 + ) + for _, cell := range argsList.Front().Value.(formulaArg).ToList() { + if ok, _ := formulaCriteriaEval(cell.Value(), criteria); ok { + count++ } - case ArgEmpty: - count++ } - return newNumberFormulaArg(float64(count)) + return newNumberFormulaArg(count) } // DEVSQ function calculates the sum of the squared deviations from the sample |