fpspreadsheet: Extend INDEX formula to return columns and rows. Add unit test for it.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9361 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2024-05-31 13:08:37 +00:00
parent a14eab917f
commit af4a35901d
3 changed files with 60 additions and 6 deletions

View File

@ -855,6 +855,8 @@ function ArgToString(Arg: TsExpressionResult): String;
procedure ArgsToFloatArray(const Args: TsExprParameterArray;
out AData: TsExprFloatArray; out AError: TsErrorValue);
function BooleanResult(AValue: Boolean): TsExpressionResult;
function CellRangeResult(AWorksheet: TsBasicWorksheet; ASheet1Index, ASheet2Index: Integer;
ARow1, ACol1, ARow2, ACol2: Cardinal): TsExpressionResult; overload;
function CellResult(AValue: String): TsExpressionResult; overload;
function CellResult(ACellRow, ACellCol: Cardinal): TsExpressionResult; overload;
function DateTimeResult(AValue: TDateTime): TsExpressionResult;
@ -4085,7 +4087,7 @@ end;
procedure TsFunctionCallBackExprNode.GetNodeValue(out AResult: TsExpressionResult);
begin
AResult.ResultType := NodeType; // was at end!
AResult.ResultType := NodeType;
if Length(FArgumentParams) > 0 then
CalcParams;
FCallBack(AResult, FArgumentParams);
@ -4103,7 +4105,7 @@ end;
procedure TFPFunctionEventHandlerExprNode.GetNodeValue(out Result: TsExpressionResult);
begin
Result.ResultType := NodeType; // was at end
Result.ResultType := NodeType;
if Length(FArgumentParams) > 0 then
CalcParams;
FCallBack(Result, FArgumentParams);
@ -4360,8 +4362,8 @@ begin
FError := errOK;
book := TsWorkbook(GetWorkbook);
F3dRange := ((ASheet1 <> '') and (ASheet2 <> '') { and (ASheet1 <> ASheet2)}) or
((ASheet1 <> '') and (ASheet2 = ''));
F3dRange := ((ASheet1 <> '') and (ASheet2 <> '')) or
((ASheet1 <> '') and (ASheet2 = ''));
FSheetIndex[1] := book.GetWorksheetIndex(ASheet1);
if (FSheetIndex[1] = -1) and (ASheet1 <> '') then
@ -4858,6 +4860,20 @@ begin
Result.ResBoolean := AValue;
end;
function CellRangeResult(AWorksheet: TsBasicWorksheet;
ASheet1Index, ASheet2Index: Integer;
ARow1, ACol1, ARow2, ACol2: Cardinal): TsExpressionResult;
begin
Result.ResultType := rtCellRange;
Result.Worksheet := AWorksheet; // Worksheet in which the formula sits
Result.ResCellRange.Sheet1 := ASheet1Index;
Result.ResCellRange.Sheet2 := ASheet2Index;
Result.ResCellRange.Row1 := ARow1;
Result.ResCellRange.Col1 := ACol1;
Result.ResCellRange.Row2 := ARow2;
Result.ResCellRange.Col2 := ACol2;
end;
function CellResult(AValue: String): TsExpressionResult;
var
p: Integer;
@ -4882,6 +4898,7 @@ begin
Result.ResultType := rtCell;
Result.ResRow := ACellRow;
Result.ResCol := ACellCol;
Result.Worksheet := nil;
end;
function DateTimeResult(AValue: TDateTime): TsExpressionResult;
@ -4934,6 +4951,8 @@ begin
Result := (cell = nil) or (cell^.ContentType = cctEmpty) or
((cell^.ContentType = cctUTF8String) and (cell^.UTF8StringValue = ''));
end;
rtCellRange:
Result := false;
end;
end;
@ -4960,6 +4979,7 @@ begin
Result := TryStrToInt64(cell^.UTF8StringValue, i);
end;
end;
rtCellRange: Result := false;
end;
end;
@ -4974,6 +4994,7 @@ begin
cell := (AValue.Worksheet as TsWorksheet).FindCell(AValue.ResRow, AValue.ResCol);
Result := (cell <> nil) and (cell^.ContentType = cctUTF8String);
end;
rtCellRange: Result := false;
end;
end;

View File

@ -2283,8 +2283,8 @@ end;
Searches for a value in an array based on its coordinates.
When the last parameter, col_no, is omitted, the input range must be a
1-d vector, either a row or a column.
The case of row_no or col_no equal to zero (returning the entire row or column)
is not supported as FPSpreadsheet cannot return a cell range, so far. }
Specification of row_no or col_no as zero returns the entire column or row
within the range. }
procedure fpsINDEX(var Result: TsExpressionResult; const Args: TsExprParameterArray);
var
rng: TsCellRange3d;
@ -2319,6 +2319,22 @@ begin
exit;
row := ArgToInt(Args[1]) + rng.Row1 - 1; // The Args are relative to the range
col := ArgToInt(Args[2]) + rng.Col1 - 1;
if ArgToInt(Args[1]) = 0 then // entire column within range
begin
if (col >= rng.Col1) and (col <= rng.Col2) then
Result := CellRangeResult(Args[0].Worksheet, rng.Sheet1, rng.Sheet2, rng.Row1, col, rng.Row2, col)
else
Result := ErrorResult(errIllegalRef);
exit;
end;
if ArgToInt(Args[2]) = 0 then // entire row within range
begin
if (row >= rng.Row1) and (row <= rng.Row2) then
Result := CellRangeResult(Args[0].Worksheet, rng.Sheet1, rng.Sheet2, row, rng.Col1, row, rng.Col2)
else
Result := ErrorResult(errIllegalRef);
exit;
end;
end;
// Check whether col/row indices are inside the range
@ -2330,6 +2346,7 @@ begin
book := TsWorksheet(Args[0].Worksheet).Workbook;
Result := CellResult(row, col);
Result.Worksheet := Args[0].Worksheet;
Result.ResSheetIndex := rng.Sheet1;
Result.ResSheetName := book.GetWorksheetByIndex(rng.Sheet1).Name;
end;
@ -2344,10 +2361,12 @@ procedure fpsINDIRECT(var Result: TsExpressionResult;
interpreted as an R1C1-style reference.
NOTE: ref_style and mixing of A1 and R1C1 notation is not supported. }
{
var
sheet: TsWorksheet;
book: TsWorkbook;
addr: String;
}
begin
Result := ErrorResult(errArgError);
if Length(Args) > 0 then

View File

@ -3482,6 +3482,20 @@
SetLength(sollValues, Row+1);
sollValues[Row] := ErrorResult(errIllegalRef);
inc(Row);
formula := 'SUM(INDEX(M1:O2,0,2))'; // Sum of numbers in 2nd column of M1:O2
MyWorksheet.WriteText(Row, 0, formula);
MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
sollValues[Row] := IntegerResult(2);
inc(Row);
formula := 'SUM(INDEX(M1:O2,2,0))'; // Sum of numbers in 2nd row of M1:O2
MyWorksheet.WriteText(Row, 0, formula);
MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
sollValues[Row] := IntegerResult(6);
// INDIRECT
inc(Row);
formula := 'INDIRECT(P1)';