From 23c73ab527731f9d414e81f7ea15e2ae1a72a290 Mon Sep 17 00:00:00 2001 From: xuri Date: Tue, 9 Feb 2021 00:12:53 +0800 Subject: [PATCH] init new formula function: HLOOKUP --- calc.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++-- calc_test.go | 25 +++++++++++++++++++-- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/calc.go b/calc.go index 1d10f62..df2230e 100644 --- a/calc.go +++ b/calc.go @@ -2214,11 +2214,12 @@ func (fn *formulaFuncs) MDETERM(argsList *list.List) (result formulaArg) { num float64 numMtx = [][]float64{} err error - strMtx = argsList.Front().Value.(formulaArg).Matrix + strMtx [][]formulaArg ) if argsList.Len() < 1 { - return + return newErrorFormulaArg(formulaErrorVALUE, "MDETERM requires at least 1 argument") } + strMtx = argsList.Front().Value.(formulaArg).Matrix var rows = len(strMtx) for _, row := range argsList.Front().Value.(formulaArg).Matrix { if len(row) != rows { @@ -3705,6 +3706,63 @@ func compareFormulaArgMatrix(lhs, rhs formulaArg, caseSensitive, exactMatch bool return criteriaEq } +// HLOOKUP function 'looks up' a given value in the top row of a data array +// (or table), and returns the corresponding value from another row of the +// array. The syntax of the function is: +// +// HLOOKUP(lookup_value,table_array,row_index_num,[range_lookup]) +// +func (fn *formulaFuncs) HLOOKUP(argsList *list.List) formulaArg { + if argsList.Len() < 3 { + return newErrorFormulaArg(formulaErrorVALUE, "HLOOKUP requires at least 3 arguments") + } + if argsList.Len() > 4 { + return newErrorFormulaArg(formulaErrorVALUE, "HLOOKUP requires at most 4 arguments") + } + lookupValue := argsList.Front().Value.(formulaArg) + tableArray := argsList.Front().Next().Value.(formulaArg) + if tableArray.Type != ArgMatrix { + return newErrorFormulaArg(formulaErrorVALUE, "HLOOKUP requires second argument of table array") + } + rowArg := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() + if rowArg.Type != ArgNumber { + return newErrorFormulaArg(formulaErrorVALUE, "HLOOKUP requires numeric row argument") + } + rowIdx, matchIdx, wasExact, exactMatch := int(rowArg.Number)-1, -1, false, false + if argsList.Len() == 4 { + rangeLookup := argsList.Back().Value.(formulaArg).ToBool() + if rangeLookup.Type == ArgError { + return newErrorFormulaArg(formulaErrorVALUE, rangeLookup.Error) + } + if rangeLookup.Number == 0 { + exactMatch = true + } + } + row := tableArray.Matrix[0] +start: + for idx, mtx := range row { + switch compareFormulaArg(mtx, lookupValue, false, exactMatch) { + case criteriaL: + matchIdx = idx + case criteriaEq: + matchIdx = idx + wasExact = true + break start + } + } + if matchIdx == -1 { + return newErrorFormulaArg(formulaErrorNA, "HLOOKUP no result found") + } + if rowIdx < 0 || rowIdx >= len(tableArray.Matrix) { + return newErrorFormulaArg(formulaErrorNA, "HLOOKUP has invalid row index") + } + row = tableArray.Matrix[rowIdx] + if wasExact || !exactMatch { + return row[matchIdx] + } + return newErrorFormulaArg(formulaErrorNA, "HLOOKUP no result found") +} + // VLOOKUP function 'looks up' a given value in the left-hand column of a // data array (or table), and returns the corresponding value from another // column of the array. The syntax of the function is: diff --git a/calc_test.go b/calc_test.go index 881077c..c72d78b 100644 --- a/calc_test.go +++ b/calc_test.go @@ -450,6 +450,7 @@ func TestCalcCellValue(t *testing.T) { "=SUMSQ(A1,B1,A2,B2,6)": "82", `=SUMSQ("",A1,B1,A2,B2,6)`: "82", `=SUMSQ(1,SUMSQ(1))`: "2", + "=SUMSQ(MUNIT(3))": "0", // TAN "=TAN(1.047197551)": "1.732050806782486", "=TAN(0)": "0", @@ -560,6 +561,9 @@ func TestCalcCellValue(t *testing.T) { "=CHOOSE(4,\"red\",\"blue\",\"green\",\"brown\")": "brown", "=CHOOSE(1,\"red\",\"blue\",\"green\",\"brown\")": "red", "=SUM(CHOOSE(A2,A1,B1:B2,A1:A3,A1:A4))": "9", + // HLOOKUP + "=HLOOKUP(D2,D2:D8,1,FALSE)": "Jan", + "=HLOOKUP(F3,F3:F8,3,FALSE)": "34440", // should be Feb // VLOOKUP "=VLOOKUP(D2,D:D,1,FALSE)": "Jan", "=VLOOKUP(D2,D:D,1,TRUE)": "Month", // should be Feb @@ -739,6 +743,8 @@ func TestCalcCellValue(t *testing.T) { // LOG10 "=LOG10()": "LOG10 requires 1 numeric argument", `=LOG10("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + // MDETERM + "MDETERM()": "MDETERM requires at least 1 argument", // MOD "=MOD()": "MOD requires 2 numeric arguments", "=MOD(6,0)": "MOD divide by zero", @@ -834,7 +840,8 @@ func TestCalcCellValue(t *testing.T) { // SUMIF "=SUMIF()": "SUMIF requires at least 2 argument", // SUMSQ - `=SUMSQ("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + `=SUMSQ("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", + "=SUMSQ(C1:D2)": "strconv.ParseFloat: parsing \"Month\": invalid syntax", // TAN "=TAN()": "TAN requires 1 numeric argument", `=TAN("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", @@ -847,7 +854,8 @@ func TestCalcCellValue(t *testing.T) { `=TRUNC(1,"X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // Statistical Functions // MEDIAN - "=MEDIAN()": "MEDIAN requires at least 1 argument", + "=MEDIAN()": "MEDIAN requires at least 1 argument", + "=MEDIAN(D1:D2)": "strconv.ParseFloat: parsing \"Month\": invalid syntax", // Information Functions // ISBLANK "=ISBLANK(A1,A2)": "ISBLANK requires 1 argument", @@ -914,6 +922,19 @@ func TestCalcCellValue(t *testing.T) { "=CHOOSE()": "CHOOSE requires 2 arguments", "=CHOOSE(\"index_num\",0)": "CHOOSE requires first argument of type number", "=CHOOSE(2,0)": "index_num should be <= to the number of values", + // HLOOKUP + "=HLOOKUP()": "HLOOKUP requires at least 3 arguments", + "=HLOOKUP(D2,D1,1,FALSE)": "HLOOKUP requires second argument of table array", + "=HLOOKUP(D2,D:D,FALSE,FALSE)": "HLOOKUP requires numeric row argument", + "=HLOOKUP(D2,D:D,1,FALSE,FALSE)": "HLOOKUP requires at most 4 arguments", + "=HLOOKUP(D2,D:D,1,2)": "strconv.ParseBool: parsing \"2\": invalid syntax", + "=HLOOKUP(D2,D10:D10,1,FALSE)": "HLOOKUP no result found", + "=HLOOKUP(D2,D2:D3,4,FALSE)": "HLOOKUP has invalid row index", + "=HLOOKUP(D2,C:C,1,FALSE)": "HLOOKUP no result found", + "=HLOOKUP(ISNUMBER(1),F3:F9,1)": "HLOOKUP no result found", + "=HLOOKUP(INT(1),E2:E9,1)": "HLOOKUP no result found", + "=HLOOKUP(MUNIT(2),MUNIT(3),1)": "HLOOKUP no result found", + "=HLOOKUP(A1:B2,B2:B3,1)": "HLOOKUP no result found", // VLOOKUP "=VLOOKUP()": "VLOOKUP requires at least 3 arguments", "=VLOOKUP(D2,D1,1,FALSE)": "VLOOKUP requires second argument of table array", -- GitLab