binary search in range lookup and new formula function: LOOKUP
This commit is contained in:
parent
3648335d7f
commit
ec45d67e59
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2016-2020 The excelize Authors.
|
||||
Copyright (c) 2016-2021 The excelize Authors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
223
calc.go
223
calc.go
|
@ -223,6 +223,7 @@ var tokenPriority = map[string]int{
|
|||
// FLOOR.MATH
|
||||
// FLOOR.PRECISE
|
||||
// GCD
|
||||
// HLOOKUP
|
||||
// IF
|
||||
// INT
|
||||
// ISBLANK
|
||||
|
@ -239,6 +240,7 @@ var tokenPriority = map[string]int{
|
|||
// LN
|
||||
// LOG
|
||||
// LOG10
|
||||
// LOOKUP
|
||||
// LOWER
|
||||
// MDETERM
|
||||
// MEDIAN
|
||||
|
@ -275,6 +277,7 @@ var tokenPriority = map[string]int{
|
|||
// TRIM
|
||||
// TRUNC
|
||||
// UPPER
|
||||
// VLOOKUP
|
||||
//
|
||||
func (f *File) CalcCellValue(sheet, cell string) (result string, err error) {
|
||||
var (
|
||||
|
@ -2335,8 +2338,8 @@ func (fn *formulaFuncs) MUNIT(argsList *list.List) (result formulaArg) {
|
|||
return newErrorFormulaArg(formulaErrorVALUE, "MUNIT requires 1 numeric argument")
|
||||
}
|
||||
dimension := argsList.Back().Value.(formulaArg).ToNumber()
|
||||
if dimension.Type == ArgError {
|
||||
return dimension
|
||||
if dimension.Type == ArgError || dimension.Number < 0 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, dimension.Error)
|
||||
}
|
||||
matrix := make([][]formulaArg, 0, int(dimension.Number))
|
||||
for i := 0; i < int(dimension.Number); i++ {
|
||||
|
@ -3607,8 +3610,7 @@ func matchPattern(pattern, name string) (matched bool) {
|
|||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
rname := make([]rune, 0, len(name))
|
||||
rpattern := make([]rune, 0, len(pattern))
|
||||
rname, rpattern := make([]rune, 0, len(name)), make([]rune, 0, len(pattern))
|
||||
for _, r := range name {
|
||||
rname = append(rname, r)
|
||||
}
|
||||
|
@ -3636,11 +3638,9 @@ func compareFormulaArg(lhs, rhs formulaArg, caseSensitive, exactMatch bool) byte
|
|||
}
|
||||
return criteriaG
|
||||
case ArgString:
|
||||
ls := lhs.String
|
||||
rs := rhs.String
|
||||
ls, rs := lhs.String, rhs.String
|
||||
if !caseSensitive {
|
||||
ls = strings.ToLower(ls)
|
||||
rs = strings.ToLower(rs)
|
||||
ls, rs = strings.ToLower(ls), strings.ToLower(rs)
|
||||
}
|
||||
if exactMatch {
|
||||
match := matchPattern(rs, ls)
|
||||
|
@ -3649,7 +3649,15 @@ func compareFormulaArg(lhs, rhs formulaArg, caseSensitive, exactMatch bool) byte
|
|||
}
|
||||
return criteriaG
|
||||
}
|
||||
return byte(strings.Compare(ls, rs))
|
||||
switch strings.Compare(ls, rs) {
|
||||
case 1:
|
||||
return criteriaG
|
||||
case -1:
|
||||
return criteriaL
|
||||
case 0:
|
||||
return criteriaEq
|
||||
}
|
||||
return criteriaErr
|
||||
case ArgEmpty:
|
||||
return criteriaEq
|
||||
case ArgList:
|
||||
|
@ -3739,16 +3747,29 @@ func (fn *formulaFuncs) HLOOKUP(argsList *list.List) formulaArg {
|
|||
}
|
||||
}
|
||||
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 exactMatch || len(tableArray.Matrix) == TotalRows {
|
||||
start:
|
||||
for idx, mtx := range row {
|
||||
lhs := mtx
|
||||
switch lookupValue.Type {
|
||||
case ArgNumber:
|
||||
if !lookupValue.Boolean {
|
||||
lhs = mtx.ToNumber()
|
||||
if lhs.Type == ArgError {
|
||||
lhs = mtx
|
||||
}
|
||||
}
|
||||
case ArgMatrix:
|
||||
lhs = tableArray
|
||||
}
|
||||
if compareFormulaArg(lhs, lookupValue, false, exactMatch) == criteriaEq {
|
||||
matchIdx = idx
|
||||
wasExact = true
|
||||
break start
|
||||
}
|
||||
}
|
||||
} else {
|
||||
matchIdx, wasExact = hlookupBinarySearch(row, lookupValue)
|
||||
}
|
||||
if matchIdx == -1 {
|
||||
return newErrorFormulaArg(formulaErrorNA, "HLOOKUP no result found")
|
||||
|
@ -3795,31 +3816,29 @@ func (fn *formulaFuncs) VLOOKUP(argsList *list.List) formulaArg {
|
|||
exactMatch = true
|
||||
}
|
||||
}
|
||||
start:
|
||||
for idx, mtx := range tableArray.Matrix {
|
||||
if len(mtx) == 0 {
|
||||
continue
|
||||
}
|
||||
lhs := mtx[0]
|
||||
switch lookupValue.Type {
|
||||
case ArgNumber:
|
||||
if !lookupValue.Boolean {
|
||||
lhs = mtx[0].ToNumber()
|
||||
if lhs.Type == ArgError {
|
||||
lhs = mtx[0]
|
||||
if exactMatch || len(tableArray.Matrix) == TotalRows {
|
||||
start:
|
||||
for idx, mtx := range tableArray.Matrix {
|
||||
lhs := mtx[0]
|
||||
switch lookupValue.Type {
|
||||
case ArgNumber:
|
||||
if !lookupValue.Boolean {
|
||||
lhs = mtx[0].ToNumber()
|
||||
if lhs.Type == ArgError {
|
||||
lhs = mtx[0]
|
||||
}
|
||||
}
|
||||
case ArgMatrix:
|
||||
lhs = tableArray
|
||||
}
|
||||
if compareFormulaArg(lhs, lookupValue, false, exactMatch) == criteriaEq {
|
||||
matchIdx = idx
|
||||
wasExact = true
|
||||
break start
|
||||
}
|
||||
case ArgMatrix:
|
||||
lhs = tableArray
|
||||
}
|
||||
switch compareFormulaArg(lhs, lookupValue, false, exactMatch) {
|
||||
case criteriaL:
|
||||
matchIdx = idx
|
||||
case criteriaEq:
|
||||
matchIdx = idx
|
||||
wasExact = true
|
||||
break start
|
||||
}
|
||||
} else {
|
||||
matchIdx, wasExact = vlookupBinarySearch(tableArray, lookupValue)
|
||||
}
|
||||
if matchIdx == -1 {
|
||||
return newErrorFormulaArg(formulaErrorNA, "VLOOKUP no result found")
|
||||
|
@ -3833,3 +3852,127 @@ start:
|
|||
}
|
||||
return newErrorFormulaArg(formulaErrorNA, "VLOOKUP no result found")
|
||||
}
|
||||
|
||||
// vlookupBinarySearch finds the position of a target value when range lookup
|
||||
// is TRUE, if the data of table array can't guarantee be sorted, it will
|
||||
// return wrong result.
|
||||
func vlookupBinarySearch(tableArray, lookupValue formulaArg) (matchIdx int, wasExact bool) {
|
||||
var low, high, lastMatchIdx int = 0, len(tableArray.Matrix) - 1, -1
|
||||
for low <= high {
|
||||
var mid int = low + (high-low)/2
|
||||
mtx := tableArray.Matrix[mid]
|
||||
lhs := mtx[0]
|
||||
switch lookupValue.Type {
|
||||
case ArgNumber:
|
||||
if !lookupValue.Boolean {
|
||||
lhs = mtx[0].ToNumber()
|
||||
if lhs.Type == ArgError {
|
||||
lhs = mtx[0]
|
||||
}
|
||||
}
|
||||
case ArgMatrix:
|
||||
lhs = tableArray
|
||||
}
|
||||
result := compareFormulaArg(lhs, lookupValue, false, false)
|
||||
if result == criteriaEq {
|
||||
matchIdx, wasExact = mid, true
|
||||
return
|
||||
} else if result == criteriaG {
|
||||
high = mid - 1
|
||||
} else if result == criteriaL {
|
||||
matchIdx, low = mid, mid+1
|
||||
if lhs.Value() != "" {
|
||||
lastMatchIdx = matchIdx
|
||||
}
|
||||
} else {
|
||||
return -1, false
|
||||
}
|
||||
}
|
||||
matchIdx, wasExact = lastMatchIdx, true
|
||||
return
|
||||
}
|
||||
|
||||
// vlookupBinarySearch finds the position of a target value when range lookup
|
||||
// is TRUE, if the data of table array can't guarantee be sorted, it will
|
||||
// return wrong result.
|
||||
func hlookupBinarySearch(row []formulaArg, lookupValue formulaArg) (matchIdx int, wasExact bool) {
|
||||
var low, high, lastMatchIdx int = 0, len(row) - 1, -1
|
||||
for low <= high {
|
||||
var mid int = low + (high-low)/2
|
||||
mtx := row[mid]
|
||||
result := compareFormulaArg(mtx, lookupValue, false, false)
|
||||
if result == criteriaEq {
|
||||
matchIdx, wasExact = mid, true
|
||||
return
|
||||
} else if result == criteriaG {
|
||||
high = mid - 1
|
||||
} else if result == criteriaL {
|
||||
low, lastMatchIdx = mid+1, mid
|
||||
} else {
|
||||
return -1, false
|
||||
}
|
||||
}
|
||||
matchIdx, wasExact = lastMatchIdx, true
|
||||
return
|
||||
}
|
||||
|
||||
// LOOKUP function performs an approximate match lookup in a one-column or
|
||||
// one-row range, and returns the corresponding value from another one-column
|
||||
// or one-row range. The syntax of the function is:
|
||||
//
|
||||
// LOOKUP(lookup_value,lookup_vector,[result_vector])
|
||||
//
|
||||
func (fn *formulaFuncs) LOOKUP(argsList *list.List) formulaArg {
|
||||
if argsList.Len() < 2 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at least 2 arguments")
|
||||
}
|
||||
if argsList.Len() > 3 {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires at most 3 arguments")
|
||||
}
|
||||
lookupValue := argsList.Front().Value.(formulaArg)
|
||||
lookupVector := argsList.Front().Next().Value.(formulaArg)
|
||||
if lookupVector.Type != ArgMatrix && lookupVector.Type != ArgList {
|
||||
return newErrorFormulaArg(formulaErrorVALUE, "LOOKUP requires second argument of table array")
|
||||
}
|
||||
cols, matchIdx := lookupCol(lookupVector), -1
|
||||
for idx, col := range cols {
|
||||
lhs := lookupValue
|
||||
switch col.Type {
|
||||
case ArgNumber:
|
||||
lhs = lhs.ToNumber()
|
||||
if !col.Boolean {
|
||||
if lhs.Type == ArgError {
|
||||
lhs = lookupValue
|
||||
}
|
||||
}
|
||||
}
|
||||
if compareFormulaArg(lhs, col, false, false) == criteriaEq {
|
||||
matchIdx = idx
|
||||
break
|
||||
}
|
||||
}
|
||||
column := cols
|
||||
if argsList.Len() == 3 {
|
||||
column = lookupCol(argsList.Back().Value.(formulaArg))
|
||||
}
|
||||
if matchIdx < 0 || matchIdx >= len(column) {
|
||||
return newErrorFormulaArg(formulaErrorNA, "LOOKUP no result found")
|
||||
}
|
||||
return column[matchIdx]
|
||||
}
|
||||
|
||||
// lookupCol extract columns for LOOKUP.
|
||||
func lookupCol(arr formulaArg) []formulaArg {
|
||||
col := arr.List
|
||||
if arr.Type == ArgMatrix {
|
||||
col = nil
|
||||
for _, r := range arr.Matrix {
|
||||
if len(r) > 0 {
|
||||
col = append(col, r[0])
|
||||
continue
|
||||
}
|
||||
col = append(col, newEmptyFormulaArg())
|
||||
}
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
|
187
calc_test.go
187
calc_test.go
|
@ -10,6 +10,17 @@ import (
|
|||
"github.com/xuri/efp"
|
||||
)
|
||||
|
||||
func prepareCalcData(cellData [][]interface{}) *File {
|
||||
f := NewFile()
|
||||
for r, row := range cellData {
|
||||
for c, value := range row {
|
||||
cell, _ := CoordinatesToCellName(c+1, r+1)
|
||||
f.SetCellValue("Sheet1", cell, value)
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func TestCalcCellValue(t *testing.T) {
|
||||
cellData := [][]interface{}{
|
||||
{1, 4, nil, "Month", "Team", "Sales"},
|
||||
|
@ -22,17 +33,6 @@ func TestCalcCellValue(t *testing.T) {
|
|||
{nil, nil, nil, "Feb", "South 1", 32080},
|
||||
{nil, nil, nil, "Feb", "South 2", 45500},
|
||||
}
|
||||
prepareData := func() *File {
|
||||
f := NewFile()
|
||||
for r, row := range cellData {
|
||||
for c, value := range row {
|
||||
cell, _ := CoordinatesToCellName(c+1, r+1)
|
||||
assert.NoError(t, f.SetCellValue("Sheet1", cell, value))
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
mathCalc := map[string]string{
|
||||
"=2^3": "8",
|
||||
"=1=1": "TRUE",
|
||||
|
@ -562,18 +562,28 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=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
|
||||
"=HLOOKUP(D2,D2:D8,1,FALSE)": "Jan",
|
||||
"=HLOOKUP(F3,F3:F8,3,FALSE)": "34440",
|
||||
"=HLOOKUP(INT(F3),F3:F8,3,FALSE)": "34440",
|
||||
"=HLOOKUP(MUNIT(1),MUNIT(1),1,FALSE)": "1",
|
||||
// VLOOKUP
|
||||
"=VLOOKUP(D2,D:D,1,FALSE)": "Jan",
|
||||
"=VLOOKUP(D2,D:D,1,TRUE)": "Month", // should be Feb
|
||||
"=VLOOKUP(INT(36693),F2:F2,1,FALSE)": "36693",
|
||||
"=VLOOKUP(INT(F2),F3:F9,1)": "32080",
|
||||
"=VLOOKUP(MUNIT(3),MUNIT(2),1)": "0", // should be 1
|
||||
"=VLOOKUP(MUNIT(3),MUNIT(3),1)": "1",
|
||||
"=VLOOKUP(D2,D:D,1,FALSE)": "Jan",
|
||||
"=VLOOKUP(D2,D1:D10,1)": "Jan",
|
||||
"=VLOOKUP(D2,D1:D11,1)": "Feb",
|
||||
"=VLOOKUP(D2,D1:D10,1,FALSE)": "Jan",
|
||||
"=VLOOKUP(INT(36693),F2:F2,1,FALSE)": "36693",
|
||||
"=VLOOKUP(INT(F2),F3:F9,1)": "32080",
|
||||
"=VLOOKUP(INT(F2),F3:F9,1,TRUE)": "32080",
|
||||
"=VLOOKUP(MUNIT(3),MUNIT(3),1)": "0",
|
||||
"=VLOOKUP(A1,A3:B5,1)": "0",
|
||||
"=VLOOKUP(MUNIT(1),MUNIT(1),1,FALSE)": "1",
|
||||
// LOOKUP
|
||||
"=LOOKUP(F8,F8:F9,F8:F9)": "32080",
|
||||
"=LOOKUP(F8,F8:F9,D8:D9)": "Feb",
|
||||
"=LOOKUP(1,MUNIT(1),MUNIT(1))": "1",
|
||||
}
|
||||
for formula, expected := range mathCalc {
|
||||
f := prepareData()
|
||||
f := prepareCalcData(cellData)
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "C1")
|
||||
assert.NoError(t, err, formula)
|
||||
|
@ -759,8 +769,9 @@ func TestCalcCellValue(t *testing.T) {
|
|||
// MULTINOMIAL
|
||||
`=MULTINOMIAL("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
|
||||
// _xlfn.MUNIT
|
||||
"=_xlfn.MUNIT()": "MUNIT requires 1 numeric argument", // not support currently
|
||||
`=_xlfn.MUNIT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax", // not support currently
|
||||
"=_xlfn.MUNIT()": "MUNIT requires 1 numeric argument",
|
||||
`=_xlfn.MUNIT("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
|
||||
"=_xlfn.MUNIT(-1)": "",
|
||||
// ODD
|
||||
"=ODD()": "ODD requires 1 numeric argument",
|
||||
`=ODD("X")`: "strconv.ParseFloat: parsing \"X\": invalid syntax",
|
||||
|
@ -947,10 +958,15 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=VLOOKUP(ISNUMBER(1),F3:F9,1)": "VLOOKUP no result found",
|
||||
"=VLOOKUP(INT(1),E2:E9,1)": "VLOOKUP no result found",
|
||||
"=VLOOKUP(MUNIT(2),MUNIT(3),1)": "VLOOKUP no result found",
|
||||
"=VLOOKUP(A1:B2,B2:B3,1)": "VLOOKUP no result found",
|
||||
"=VLOOKUP(1,G1:H2,1,FALSE)": "VLOOKUP no result found",
|
||||
// LOOKUP
|
||||
"=LOOKUP()": "LOOKUP requires at least 2 arguments",
|
||||
"=LOOKUP(D2,D1,D2)": "LOOKUP requires second argument of table array",
|
||||
"=LOOKUP(D2,D1,D2,FALSE)": "LOOKUP requires at most 3 arguments",
|
||||
"=LOOKUP(D1,MUNIT(1),MUNIT(1))": "LOOKUP no result found",
|
||||
}
|
||||
for formula, expected := range mathCalcError {
|
||||
f := prepareData()
|
||||
f := prepareCalcData(cellData)
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "C1")
|
||||
assert.EqualError(t, err, expected, formula)
|
||||
|
@ -974,7 +990,7 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=A1/A2/SUM(A1:A2:B1)*A3": "0.125",
|
||||
}
|
||||
for formula, expected := range referenceCalc {
|
||||
f := prepareData()
|
||||
f := prepareCalcData(cellData)
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "C1")
|
||||
assert.NoError(t, err)
|
||||
|
@ -988,7 +1004,7 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=1+SUM(SUM(A1+A2/A4)*(2-3),2)": "#DIV/0!",
|
||||
}
|
||||
for formula, expected := range referenceCalcError {
|
||||
f := prepareData()
|
||||
f := prepareCalcData(cellData)
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "C1")
|
||||
assert.EqualError(t, err, expected)
|
||||
|
@ -1000,23 +1016,23 @@ func TestCalcCellValue(t *testing.T) {
|
|||
"=RANDBETWEEN(1,2)",
|
||||
}
|
||||
for _, formula := range volatileFuncs {
|
||||
f := prepareData()
|
||||
f := prepareCalcData(cellData)
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
|
||||
_, err := f.CalcCellValue("Sheet1", "C1")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Test get calculated cell value on not formula cell.
|
||||
f := prepareData()
|
||||
f := prepareCalcData(cellData)
|
||||
result, err := f.CalcCellValue("Sheet1", "A1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", result)
|
||||
// Test get calculated cell value on not exists worksheet.
|
||||
f = prepareData()
|
||||
f = prepareCalcData(cellData)
|
||||
_, err = f.CalcCellValue("SheetN", "A1")
|
||||
assert.EqualError(t, err, "sheet SheetN is not exist")
|
||||
// Test get calculated cell value with not support formula.
|
||||
f = prepareData()
|
||||
f = prepareCalcData(cellData)
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "A1", "=UNSUPPORT(A1)"))
|
||||
_, err = f.CalcCellValue("Sheet1", "A1")
|
||||
assert.EqualError(t, err, "not support UNSUPPORT function")
|
||||
|
@ -1036,24 +1052,13 @@ func TestCalculate(t *testing.T) {
|
|||
assert.EqualError(t, calculate(opd, opt), err)
|
||||
}
|
||||
|
||||
func TestCalcCellValueWithDefinedName(t *testing.T) {
|
||||
func TestCalcWithDefinedName(t *testing.T) {
|
||||
cellData := [][]interface{}{
|
||||
{"A1 value", "B1 value", nil},
|
||||
}
|
||||
prepareData := func() *File {
|
||||
f := NewFile()
|
||||
for r, row := range cellData {
|
||||
for c, value := range row {
|
||||
cell, _ := CoordinatesToCellName(c+1, r+1)
|
||||
assert.NoError(t, f.SetCellValue("Sheet1", cell, value))
|
||||
}
|
||||
}
|
||||
assert.NoError(t, f.SetDefinedName(&DefinedName{Name: "defined_name1", RefersTo: "Sheet1!A1", Scope: "Workbook"}))
|
||||
assert.NoError(t, f.SetDefinedName(&DefinedName{Name: "defined_name1", RefersTo: "Sheet1!B1", Scope: "Sheet1"}))
|
||||
|
||||
return f
|
||||
}
|
||||
f := prepareData()
|
||||
f := prepareCalcData(cellData)
|
||||
assert.NoError(t, f.SetDefinedName(&DefinedName{Name: "defined_name1", RefersTo: "Sheet1!A1", Scope: "Workbook"}))
|
||||
assert.NoError(t, f.SetDefinedName(&DefinedName{Name: "defined_name1", RefersTo: "Sheet1!B1", Scope: "Sheet1"}))
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", "=defined_name1"))
|
||||
result, err := f.CalcCellValue("Sheet1", "C1")
|
||||
assert.NoError(t, err)
|
||||
|
@ -1061,7 +1066,7 @@ func TestCalcCellValueWithDefinedName(t *testing.T) {
|
|||
assert.Equal(t, "B1 value", result, "=defined_name1")
|
||||
}
|
||||
|
||||
func TestCalcPow(t *testing.T) {
|
||||
func TestCalcArithmeticOperations(t *testing.T) {
|
||||
err := `strconv.ParseFloat: parsing "text": invalid syntax`
|
||||
assert.EqualError(t, calcPow("1", "text", nil), err)
|
||||
assert.EqualError(t, calcPow("text", "1", nil), err)
|
||||
|
@ -1085,7 +1090,7 @@ func TestCalcPow(t *testing.T) {
|
|||
assert.EqualError(t, calcDiv("text", "1", nil), err)
|
||||
}
|
||||
|
||||
func TestISBLANK(t *testing.T) {
|
||||
func TestCalcISBLANK(t *testing.T) {
|
||||
argsList := list.New()
|
||||
argsList.PushBack(formulaArg{
|
||||
Type: ArgUnknown,
|
||||
|
@ -1096,7 +1101,7 @@ func TestISBLANK(t *testing.T) {
|
|||
assert.Empty(t, result.Error)
|
||||
}
|
||||
|
||||
func TestAND(t *testing.T) {
|
||||
func TestCalcAND(t *testing.T) {
|
||||
argsList := list.New()
|
||||
argsList.PushBack(formulaArg{
|
||||
Type: ArgUnknown,
|
||||
|
@ -1107,7 +1112,7 @@ func TestAND(t *testing.T) {
|
|||
assert.Empty(t, result.Error)
|
||||
}
|
||||
|
||||
func TestOR(t *testing.T) {
|
||||
func TestCalcOR(t *testing.T) {
|
||||
argsList := list.New()
|
||||
argsList.PushBack(formulaArg{
|
||||
Type: ArgUnknown,
|
||||
|
@ -1118,7 +1123,7 @@ func TestOR(t *testing.T) {
|
|||
assert.Empty(t, result.Error)
|
||||
}
|
||||
|
||||
func TestDet(t *testing.T) {
|
||||
func TestCalcDet(t *testing.T) {
|
||||
assert.Equal(t, det([][]float64{
|
||||
{1, 2, 3, 4},
|
||||
{2, 3, 4, 5},
|
||||
|
@ -1127,7 +1132,12 @@ func TestDet(t *testing.T) {
|
|||
}), float64(0))
|
||||
}
|
||||
|
||||
func TestCompareFormulaArg(t *testing.T) {
|
||||
func TestCalcToBool(t *testing.T) {
|
||||
b := newBoolFormulaArg(true).ToBool()
|
||||
assert.Equal(t, b.Boolean, true)
|
||||
assert.Equal(t, b.Number, 1.0)
|
||||
}
|
||||
func TestCalcCompareFormulaArg(t *testing.T) {
|
||||
assert.Equal(t, compareFormulaArg(newEmptyFormulaArg(), newEmptyFormulaArg(), false, false), criteriaEq)
|
||||
lhs := newListFormulaArg([]formulaArg{newEmptyFormulaArg()})
|
||||
rhs := newListFormulaArg([]formulaArg{newEmptyFormulaArg(), newEmptyFormulaArg()})
|
||||
|
@ -1141,9 +1151,82 @@ func TestCompareFormulaArg(t *testing.T) {
|
|||
assert.Equal(t, compareFormulaArg(formulaArg{Type: ArgUnknown}, formulaArg{Type: ArgUnknown}, false, false), criteriaErr)
|
||||
}
|
||||
|
||||
func TestMatchPattern(t *testing.T) {
|
||||
func TestCalcMatchPattern(t *testing.T) {
|
||||
assert.True(t, matchPattern("", ""))
|
||||
assert.True(t, matchPattern("file/*", "file/abc/bcd/def"))
|
||||
assert.True(t, matchPattern("*", ""))
|
||||
assert.False(t, matchPattern("file/?", "file/abc/bcd/def"))
|
||||
}
|
||||
|
||||
func TestCalcVLOOKUP(t *testing.T) {
|
||||
cellData := [][]interface{}{
|
||||
{nil, nil, nil, nil, nil, nil},
|
||||
{nil, "Score", "Grade", nil, nil, nil},
|
||||
{nil, 0, "F", nil, "Score", 85},
|
||||
{nil, 60, "D", nil, "Grade"},
|
||||
{nil, 70, "C", nil, nil, nil},
|
||||
{nil, 80, "b", nil, nil, nil},
|
||||
{nil, 90, "A", nil, nil, nil},
|
||||
{nil, 85, "B", nil, nil, nil},
|
||||
{nil, nil, nil, nil, nil, nil},
|
||||
}
|
||||
f := prepareCalcData(cellData)
|
||||
calc := map[string]string{
|
||||
"=VLOOKUP(F3,B3:C8,2)": "b",
|
||||
"=VLOOKUP(F3,B3:C8,2,TRUE)": "b",
|
||||
"=VLOOKUP(F3,B3:C8,2,FALSE)": "B",
|
||||
}
|
||||
for formula, expected := range calc {
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "F4", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "F4")
|
||||
assert.NoError(t, err, formula)
|
||||
assert.Equal(t, expected, result, formula)
|
||||
}
|
||||
calcError := map[string]string{
|
||||
"=VLOOKUP(INT(1),C3:C3,1,FALSE)": "VLOOKUP no result found",
|
||||
}
|
||||
for formula, expected := range calcError {
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "F4", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "F4")
|
||||
assert.EqualError(t, err, expected, formula)
|
||||
assert.Equal(t, "", result, formula)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcHLOOKUP(t *testing.T) {
|
||||
cellData := [][]interface{}{
|
||||
{"Example Result Table"},
|
||||
{nil, "A", "B", "C", "E", "F"},
|
||||
{"Math", .58, .9, .67, .76, .8},
|
||||
{"French", .61, .71, .59, .59, .76},
|
||||
{"Physics", .75, .45, .39, .52, .69},
|
||||
{"Biology", .39, .55, .77, .61, .45},
|
||||
{},
|
||||
{"Individual Student Score"},
|
||||
{"Student:", "Biology Score:"},
|
||||
{"E"},
|
||||
}
|
||||
f := prepareCalcData(cellData)
|
||||
formulaList := map[string]string{
|
||||
"=HLOOKUP(A10,A2:F6,5,FALSE)": "0.61",
|
||||
"=HLOOKUP(D3,D3:D3,1,TRUE)": "0.67",
|
||||
"=HLOOKUP(F3,D3:F3,1,TRUE)": "0.8",
|
||||
"=HLOOKUP(A5,A2:F2,1,TRUE)": "F",
|
||||
"=HLOOKUP(\"D\",A2:F2,1,TRUE)": "C",
|
||||
}
|
||||
for formula, expected := range formulaList {
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "B10", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "B10")
|
||||
assert.NoError(t, err, formula)
|
||||
assert.Equal(t, expected, result, formula)
|
||||
}
|
||||
calcError := map[string]string{
|
||||
"=HLOOKUP(INT(1),A3:A3,1,FALSE)": "HLOOKUP no result found",
|
||||
}
|
||||
for formula, expected := range calcError {
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "B10", formula))
|
||||
result, err := f.CalcCellValue("Sheet1", "B10")
|
||||
assert.EqualError(t, err, expected, formula)
|
||||
assert.Equal(t, "", result, formula)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue