unit GridProcs; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Grids, Globals, DictionaryUnit; function CollectValues(AGrid: TStringGrid; AColIndex: Integer; AColCheck: IntDyneVec = nil): DblDyneVec; procedure GetMinMax(AGrid: TStringGrid; AColIndex: Integer; const AColCheck: IntDyneVec; out AMin, AMax: Double); function GoodRecord(AGrid: TStringGrid; ARow: integer; const AColCheck: IntDyneVec): boolean; function IsEmptyNumericValue(AGrid: TStringGrid; ARow, ACol: Integer): Boolean; function IsFiltered(AGrid: TStringGrid; ARow: integer): boolean; function IsMissingValueCode(AGrid: TStringGrid; ARow, ACol: Integer): Boolean; function ValidValue(AGrid: TStringGrid; ARow, ACol: integer): boolean; implementation uses Math; { Extracts the values in the given column from the grid and returns them as an array. Cells which are filtered or empty are not considered. This check is extended over all columns specified by the column indices in AColCheck; AColCheck should be empty to consider only the current column. Non-numeric values in the considered cell will raise an exception. NOTE: AColCheck must not be overdimensioned! } function CollectValues(AGrid: TStringGrid; AColIndex: Integer; AColCheck: IntDyneVec): DblDyneVec; var row, n: Integer; val: Double; begin SetLength(Result, AGrid.RowCount); n := 0; for row := 1 to AGrid.RowCount-1 do begin if Length(AColCheck) = 0 then begin if not ValidValue(AGrid, row, AColIndex) then continue; end else begin if not GoodRecord(AGrid, row, AColCheck) then continue; end; if TryStrToFloat(trim(AGrid.Cells[AColIndex, row]), val) then Result[n] := val else raise ELazStats.CreateFmt('Non-numeric string "%s" in column %d, row %d', [AGrid.Cells[AColIndex, row], AColIndex, row]); inc(n); end; SetLength(Result, n); end; { Determines the minimum and maximum of the values in the specified column of the grid. Rows with "invalid" data are ignored. If AColCheck contains other column indices these cells must be "valid", too. } procedure GetMinMax(AGrid: TStringGrid; AColIndex: Integer; const AColCheck: IntDyneVec; out AMin, AMax: Double); var row: Integer; value: Double; begin AMin := Infinity; AMax := -Infinity; for row := 1 to AGrid.RowCount-1 do begin if Length(AColCheck) = 0 then begin if not ValidValue(AGrid, row, AColIndex) then continue; end else begin if not GoodRecord(AGrid, row, AColCheck) then continue; end; value := StrToFloat(trim(AGrid.Cells[AColIndex, row])); if value < AMin then AMin := value; if value > AMax then AMax := value; end; end; { Checks whether all cells specified for the given row in the columns listed in the GridPos array are "valid": not filtered and not empty } function GoodRecord(AGrid: TStringGrid; ARow: integer; const AColCheck: IntDyneVec): boolean; var i, j: integer; begin Result := true; for i := 0 to High(AColCheck) do begin j := AColCheck[i]; if not ValidValue(AGrid, ARow, j) then Result := false; end; end; { Checks whether the cell in the given row in the given numeric column is empty. } function IsEmptyNumericValue(AGrid: TStringGrid; ARow, ACol: Integer): Boolean; var value: String; isStringField: Boolean; begin value := Trim(AGrid.Cells[ACol, ARow]); isStringField := DictionaryFrm.DictGrid.Cells[4, ACol] = 'S'; Result := not IsStringField and (value = ''); end; { Checks whether the specified row is "filtered". Two criteria are needed for a row to be filtered: - The cell in column FilterCol (global value) must contain the text 'NO'. - The global variable "FilterOn" must be TRUE. } function IsFiltered(AGrid: TStringGrid; ARow: integer): boolean; begin Result := FilterOn and (Trim(AGrid.Cells[FilterCol, ARow]) = 'NO'); end; { Checks whether specified cell contains the "missing value code" defined by the Dictionary } function IsMissingValueCode(AGrid: TStringGrid; ARow, ACol: Integer): Boolean; var missingCode: String; value: String; begin missingCode := Trim(DictionaryFrm.DictGrid.Cells[6, ACol]); value := Trim(AGrid.Cells[ACol, ARow]); Result := (value = missingCode); end; { Checks wheter the value in cell at the given column and row is a not-filtered, non-empty number. NOTE: non-numeric characters in a numeric field are not taken into account! } function ValidValue(AGrid: TStringGrid; ARow, ACol: integer): boolean; begin Result := not ( IsFiltered(AGrid, ARow) or // filtering is active and row is marked to be excluded IsEmptyNumericValue(AGrid, ARow, aCol) or // column is numeric, but cell is empty IsMissingValueCode(AGrid, ARow, ACol) // cell contains the "missing value code" ); end; end.