diff --git a/calc.go b/calc.go index d921c3540a..20d5b5e707 100644 --- a/calc.go +++ b/calc.go @@ -550,6 +550,8 @@ type formulaFuncs struct { // MIRR // MOD // MODE +// MODE.MULT +// MODE.SNGL // MONTH // MROUND // MULTINOMIAL @@ -8021,6 +8023,67 @@ func (fn *formulaFuncs) MODE(argsList *list.List) formulaArg { return newNumberFormulaArg(mode) } +// MODEdotMULT function returns a vertical array of the statistical modes +// (the most frequently occurring values) within a list of supplied numbers. +// The syntax of the function is: +// +// MODE.MULT(number1,[number2],...) +// +func (fn *formulaFuncs) MODEdotMULT(argsList *list.List) formulaArg { + if argsList.Len() < 1 { + return newErrorFormulaArg(formulaErrorVALUE, "MODE.MULT requires at least 1 argument") + } + var values []float64 + for arg := argsList.Front(); arg != nil; arg = arg.Next() { + cells := arg.Value.(formulaArg) + if cells.Type != ArgMatrix && cells.ToNumber().Type != ArgNumber { + return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE) + } + for _, cell := range cells.ToList() { + if num := cell.ToNumber(); num.Type == ArgNumber { + values = append(values, num.Number) + } + } + } + sort.Float64s(values) + cnt := len(values) + var count, modeCnt int + var mtx [][]formulaArg + for i := 0; i < cnt; i++ { + count = 0 + for j := i + 1; j < cnt; j++ { + if values[i] == values[j] { + count++ + } + } + if count > modeCnt { + modeCnt = count + mtx = [][]formulaArg{} + mtx = append(mtx, []formulaArg{newNumberFormulaArg(values[i])}) + } else if count == modeCnt { + mtx = append(mtx, []formulaArg{newNumberFormulaArg(values[i])}) + } + } + if modeCnt == 0 { + return newErrorFormulaArg(formulaErrorNA, formulaErrorNA) + } + return newMatrixFormulaArg(mtx) +} + +// MODEdotSNGL function returns the statistical mode (the most frequently +// occurring value) within a list of supplied numbers. If there are 2 or more +// most frequently occurring values in the supplied data, the function returns +// the lowest of these values. The syntax of the function is: +// +// MODE.SNGL(number1,[number2],...) +// +func (fn *formulaFuncs) MODEdotSNGL(argsList *list.List) formulaArg { + if argsList.Len() < 1 { + return newErrorFormulaArg(formulaErrorVALUE, "MODE.SNGL requires at least 1 argument") + } + return fn.MODE(argsList) +} + // NEGBINOMdotDIST function calculates the probability mass function or the // cumulative distribution function for the Negative Binomial Distribution. // This gives the probability that there will be a given number of failures diff --git a/calc_test.go b/calc_test.go index f6499def76..689fd92b6b 100644 --- a/calc_test.go +++ b/calc_test.go @@ -4880,8 +4880,11 @@ func TestCalcMODE(t *testing.T) { } f := prepareCalcData(cellData) formulaList := map[string]string{ - "=MODE(A1:A10)": "3", - "=MODE(B1:B6)": "2", + "=MODE(A1:A10)": "3", + "=MODE(B1:B6)": "2", + "=MODE.MULT(A1:A10)": "", + "=MODE.SNGL(A1:A10)": "3", + "=MODE.SNGL(B1:B6)": "2", } for formula, expected := range formulaList { assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula)) @@ -4890,9 +4893,15 @@ func TestCalcMODE(t *testing.T) { assert.Equal(t, expected, result, formula) } calcError := map[string]string{ - "=MODE()": "MODE requires at least 1 argument", - "=MODE(0,\"\")": "#VALUE!", - "=MODE(D1:D3)": "#N/A", + "=MODE()": "MODE requires at least 1 argument", + "=MODE(0,\"\")": "#VALUE!", + "=MODE(D1:D3)": "#N/A", + "=MODE.MULT()": "MODE.MULT requires at least 1 argument", + "=MODE.MULT(0,\"\")": "#VALUE!", + "=MODE.MULT(D1:D3)": "#N/A", + "=MODE.SNGL()": "MODE.SNGL requires at least 1 argument", + "=MODE.SNGL(0,\"\")": "#VALUE!", + "=MODE.SNGL(D1:D3)": "#N/A", } for formula, expected := range calcError { assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))