I have StringGrid with 2 columns: Player
and Scores
. I have to sort this table looking at the score of a player.
This is the situation. I have tried with a StringGrid3.SortColRow(true, 1);
but it sorts only the string value. If I have numbers like [1, 4, 12, 3] the sorted StringGrid becomes [a, 12, 3, 4].
I have to sort the Integer value, not the string one. Also, my problem is that I must move the player's name too with the numbers (as you can see in the picture above).
How could I do it?
You might write a comparator which is trying to sort by Numbers if both compared values are numbers. With casting the Stringgrid to its ancestor TCustomgrid you are able to use the procedure MoveRow to swap rows. An example showing sorting over many columns could look like this:
unit StringGridSortEnh;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, StdCtrls;
type
TForm1 = class(TForm)
StringGrid1: TStringGrid;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TMoveSG = class(TCustomGrid);
TSortInfo = Record
col: Integer;
asc: Boolean;
End;
function CompareNumber(i1, i2: Double): Integer;
// Result: -1 if i1 < i2, 1 if i1 > i2, 0 if i1 = i2
begin
if i1 < i2 then
Result := -1
else if i1 > i2 then
Result := 1
else
Result := 0;
end;
// Compare Strings if possible try to interpret as numbers
function CompareValues(const S1, S2 : String;asc:Boolean): Integer;
var
V1, V2 : Double;
C1, C2 : Integer;
begin
Val(S1, V1, C1);
Val(S2, V2, C2);
if (C1 = 0) and (C2 = 0) then // both as numbers
Result := CompareNumber(V1, V2)
else // not both as nubers
Result := AnsiCompareStr(S1, S2);
if not Asc then Result := Result * -1;
end;
procedure SortGridByCols(Grid: TStringGrid; ColOrder: array of TSortInfo; Fixed: Boolean);
var
I, J, FirstRow: Integer;
Sorted: Boolean;
function Sort(Row1, Row2: Integer): Integer;
var
C: Integer;
begin
C := 0;
Result := CompareValues(Grid.Cols[ColOrder[C].col][Row1], Grid.Cols[ColOrder[C].col][Row2],ColOrder[C].asc);
if Result = 0 then
begin
Inc(C);
while (C <= High(ColOrder)) and (Result = 0) do
begin
Result := CompareValues(Grid.Cols[ColOrder[C].col][Row1], Grid.Cols[ColOrder[C].col][Row2],ColOrder[C].asc);
Inc(C);
end;
end;
end;
begin
for I := 0 to High(ColOrder) do
if (ColOrder[I].col < 0) or (ColOrder[I].col >= Grid.ColCount) then
Exit;
if Fixed then
FirstRow := 0
else
FirstRow := Grid.FixedRows;
J := FirstRow;
Sorted := True;
repeat
Inc(J);
for I := FirstRow to Grid.RowCount - 2 do
if Sort(I, I + 1) > 0 then
begin
TMoveSG(Grid).MoveRow(i + 1, i);
Sorted := False;
end;
until Sorted or (J >= Grid.RowCount + 1000);
Grid.Repaint;
end;
procedure TForm1.Button1Click(Sender: TObject);
const // we want to use only 4 columns
MyArray: array[0..3] of TSortInfo =
((col: 1; asc: true),
(col: 2; asc: true),
(col: 3; asc: true),
(col: 4; asc: false)
);
begin
SortGridByCols(StringGrid1,MyArray,true);
end;
procedure TForm1.Button2Click(Sender: TObject);
const // we want to use only one column
MyArray: array[0..0] of TSortInfo = ((col: 1; asc: true));
begin
SortGridByCols(StringGrid1,MyArray,true);
end;
end.
In Lazarus you can define how the cells of a TStringGrid sort using the OnCompareCells
event, see the following link for and example of how to do this with numbers and more info.
http://wiki.lazarus.freepascal.org/Grids_Reference_Page#Sorting_Columns_or_Rows
If you want to sort the rows in Delphi you would need to re-implement the SortColRow
method from Lazarus.
You can do this with a class helper
Note that this has NOT been tested fully!
The AnsiCompareStr
in the below procedures can then be swamped out to compare integers.
type
TStringGridHelper = class helper for TStringGrid
public
procedure SortColRow(IsColumn: Boolean; Index: Integer); overload;
procedure SortColRow(IsColumn: Boolean; Index: Integer; FromIndex: Integer; ToIndex: Integer); overload;
end;
implementation
procedure TStringGridHelper.SortColRow(IsColumn: Boolean; Index: Integer);
begin
if (IsColumn) then SortColRow(IsColumn, Index, FixedCols, ColCount - 1)
else SortColRow(IsColumn, Index, FixedRows, RowCount - 1)
end;
procedure TStringGridHelper.SortColRow(IsColumn: Boolean; Index: Integer; FromIndex: Integer; ToIndex: Integer);
var i, p, x, c : Integer;
s1, s2 : String;
begin
if (IsColumn) then
begin
for x := ToIndex downto FromIndex do
for I := FromIndex to ToIndex - 1 do
begin
s1 := Cells[i, index];
s2 := Cells[i + 1, index];
c := AnsiCompareStr(s1, s2);
if (c > 0) then
begin
p := i + 1;
p := Max(p, FromIndex);
p := Min(p, ToIndex);
MoveColumn(i, p);
end;
end;
end
else
begin
for x := ToIndex downto FromIndex do
for I := FromIndex to ToIndex - 1 do
begin
s1 := Cells[index, i];
s2 := Cells[index, i + 1];
c := AnsiCompareStr(s1, s2);
if (c > 0) then
begin
p := i + 1;
p := Max(p, FromIndex);
p := Min(p, ToIndex);
MoveRow(i, p);
end;
end;
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With