diff --git a/components/fpspreadsheet/fpsfunc.pas b/components/fpspreadsheet/fpsfunc.pas index ae49a7b00..7d9ff2f72 100644 --- a/components/fpspreadsheet/fpsfunc.pas +++ b/components/fpspreadsheet/fpsfunc.pas @@ -11,6 +11,8 @@ type TsArgumentType = (atCell, atCellRange, atNumber, atString, atBool, atError, atEmpty); + TsArgumentTypes = set of TsArgumentType; + TsArgBoolArray = array of boolean; TsArgNumberArray = array of double; TsArgStringArray = array of string; @@ -128,6 +130,7 @@ function fpsYEAR (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsAVEDEV (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsAVERAGE (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsCOUNT (Args: TsArgumentStack; NumArgs: Integer): TsArgument; +function fpsCOUNTA (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsCOUNTBLANK (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsCOUNTIF (Args: TsArgumentStack; NumArgs: Integer): TsArgument; function fpsMAX (Args: TsArgumentStack; NumArgs: Integer): TsArgument; @@ -1534,7 +1537,7 @@ end; function fpsCOUNT(Args: TsArgumentStack; NumArgs: Integer): TsArgument; { counts the number of cells that contain numbers as well as the number of arguments that contain numbers. - COUNT( argument1, [argument2, ... argument_n] ) + COUNT( argument1 [, argument2, ... argument_n] ) } var data: TsArgNumberArray; @@ -1543,6 +1546,38 @@ begin Result := CreateNumberArg(Length(data)); end; +function fpsCOUNTA(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +// COUNTA (argument1 [, argument2, ... argument_n] ) +// Counts the number of non-empty cells specified by arguments of misc type. +var + arg: TsArgument; + counter: Integer; + r, c: Integer; + cell: PCell; + n: Integer; +begin + n := 0; + // The order of arguments is not important for counting --> we just pop them from the stack. + for counter := 1 to NumArgs do begin + arg := Args.Pop; + case arg.ArgumentType of + atCell: + if arg.Cell^.ContentType <> cctEmpty then inc(n); + atCellRange: + for r := arg.FirstRow to arg.LastRow do + for c := arg.FirstCol to arg.LastCol do begin + cell := arg.Worksheet.FindCell(r, c); + if (cell <> nil) and (cell^.ContentType <> cctEmpty) then inc(n); + end; + atString: + if arg.StringValue <> '' then inc(n); + atNumber, atBool, atError: + inc(n); + end; + end; + Result := CreateNumberArg(n); +end; + function fpsCOUNTBLANK(Args: TsArgumentStack; NumArgs: Integer): TsArgument; // COUNTBLANK( range ) // counts the number of empty cells in a range. diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 6025a3baa..abc6b5fe7 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1152,7 +1152,7 @@ var (Symbol:'<'; MinParams:2; MaxParams:2; Func:fpsLess), // fekLess (Symbol:'<='; MinParams:2; MaxParams:2; Func:fpsLessEqual), // fekLessEqual (Symbol:'<>'; MinParams:2; MaxParams:2; Func:fpsNotEqual), // fekNotEqual - (Symbol:''; MinParams:1; MaxParams:1; Func:nil), // fekParen + (Symbol:''; MinParams:1; MaxParams:1; Func:nil), // fekParen -- no need to calculate! { math } (Symbol:'ABS'; MinParams:1; MaxParams:1; Func:fpsABS), // fekABS (Symbol:'ACOS'; MinParams:1; MaxParams:1; Func:fpsACOS), // fekACOS @@ -1203,7 +1203,7 @@ var (Symbol:'CHIDIST'; MinParams:2; MaxParams:2; Func:nil), // fekCHIDIST (Symbol:'CHIINV'; MinParams:2; MaxParams:2; Func:nil), // fekCHIINV (Symbol:'COUNT'; MinParams:0; MaxParams:30; Func:fpsCOUNT), // fekCOUNT - (Symbol:'COUNTA'; MinParams:0; MaxParams:30; Func:nil), // fekCOUNTA + (Symbol:'COUNTA'; MinParams:0; MaxParams:30; Func:fpsCOUNTA), // fekCOUNTA (Symbol:'COUNTBLANK';MinParams:1; MaxParams:1; Func:fpsCOUNTBLANK), // fekCOUNTBLANK (Symbol:'COUNTIF'; MinParams:2; MaxParams:2; Func:fpsCOUNTIF), // fekCOUNTIF (Symbol:'MAX'; MinParams:1; MaxParams:30; Func:fpsMAX), // fekMAX diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index 505633548..ed06b19d3 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -80,7 +80,6 @@ - @@ -89,12 +88,10 @@ - - diff --git a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc index 439a4894f..718d1c3be 100644 --- a/components/fpspreadsheet/tests/testcases_calcrpnformula.inc +++ b/components/fpspreadsheet/tests/testcases_calcrpnformula.inc @@ -1286,6 +1286,42 @@ SetLength(sollValues, Row+1); sollValues[Row] := CreateNumberArg(2); + // COUNTA + MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTA(1, 1.1, 1.2, 0.9, 0.8)'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNFunc(fekCOUNTA, 5, nil)))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumberArg(5); + + MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTA(1, 1.1, 1.2, 0.9, 0.8, "A", "B")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNNumber(1.0, + RPNNumber(1.1, + RPNNumber(1.2, + RPNNumber(0.9, + RPNNumber(0.8, + RPNString('A', + RPNString('B', + RPNFunc(fekCOUNTA, 7, nil)))))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumberArg(7); + + inc(Row); + MyWorksheet.WriteUTF8Text(Row, 0, '=COUNTA(A1:C2,1,2,"A")'); + MyWorksheet.WriteRPNFormula(Row, 1, CreateRPNFormula( + RPNCellRange('A1:C2', + RPNNumber(1, + RPNNumber(2, + RPNString('A', + RPNFunc(fekCOUNTA, 4, nil))))))); + SetLength(sollValues, Row+1); + sollValues[Row] := CreateNumberArg(7); + if AFormat <> sfExcel2 then begin // COUNTBLANK inc(Row);