Sort TStringGrid by row and its integer value
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?
delphi sorting
add a comment |
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?
delphi sorting
add a comment |
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?
delphi sorting
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?
delphi sorting
delphi sorting
asked Apr 29 '14 at 13:27
Alberto Rossi
86622048
86622048
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
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.
Wait but you didn't decalre TMoveSG(Grid).MoveRow(); anywhere
– Alberto Rossi
Apr 30 '14 at 15:13
@AlbertoRossi There is no need to declare it since you can access protected procedures from the same unit. TMoveSG = class(TCustomGrid); does the trick having the protected MoveRow() in the same unit.
– bummi
Apr 30 '14 at 15:18
Thank you! But I still have the error (I use Lazarus). It says that 'identifier idents no member "MoveRow" '
– Alberto Rossi
Apr 30 '14 at 15:19
1
@AlbertoRossi taking a quick peek into Lazarus (I'm not familiar with)TMoveSG(StringGrid1).DoOPMoveColRow(false,1,3);
should do the trick here, since MoveRow is not implemented. You will have to verify ....
– bummi
Apr 30 '14 at 15:36
Note: MoveRow only moves one row. It will not SWAP the indicated rows.
– Rigel
Jun 11 '14 at 9:52
add a comment |
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;
add a comment |
Are you using Lazarus instead of Delphi? I'm not aware of a sorting feature in the standard Delphi StringGrid implementation.
The issue is that the StringGrid is just that, a grid of Strings. It has no understanding that the strings are in fact numbers, so it can only sort them alphabetically.
If you are looking for a more complex sorting ability than the grid provides naturally you have a couple of choices. Either override the TStringGrid
class and produce your own specialised version that sorts how you want it to (i'm afraid i don't have the Lazarus source code to hand to give you more specific instructions), or you can simply sort the records before you put them into the grid.
For instance, if you have a TPlayer
class that represents your player and their scores then you can use TList.Sort(..)
to sort it however you like before you loop over and populate the grid.
//
// Sort the player scores by their *numeric* value, not by the string representation
//
function SortByPlayerScore(A, B: Pointer) : integer
begin
Result := TPlayer(A).Score - TPlayer(B).Score;
end;
// ....
list.Sort(SortByPlayerScore);
grid.RowCount := list.Count + 1;
for i := 0 to list.Count - 1 do begin
grid.Cells[0, i + 1] := list[i].Name;
grid.Cells[1, i + 1] := IntToStr(list[i].Score);
end;
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f23366062%2fsort-tstringgrid-by-row-and-its-integer-value%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
Wait but you didn't decalre TMoveSG(Grid).MoveRow(); anywhere
– Alberto Rossi
Apr 30 '14 at 15:13
@AlbertoRossi There is no need to declare it since you can access protected procedures from the same unit. TMoveSG = class(TCustomGrid); does the trick having the protected MoveRow() in the same unit.
– bummi
Apr 30 '14 at 15:18
Thank you! But I still have the error (I use Lazarus). It says that 'identifier idents no member "MoveRow" '
– Alberto Rossi
Apr 30 '14 at 15:19
1
@AlbertoRossi taking a quick peek into Lazarus (I'm not familiar with)TMoveSG(StringGrid1).DoOPMoveColRow(false,1,3);
should do the trick here, since MoveRow is not implemented. You will have to verify ....
– bummi
Apr 30 '14 at 15:36
Note: MoveRow only moves one row. It will not SWAP the indicated rows.
– Rigel
Jun 11 '14 at 9:52
add a comment |
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.
Wait but you didn't decalre TMoveSG(Grid).MoveRow(); anywhere
– Alberto Rossi
Apr 30 '14 at 15:13
@AlbertoRossi There is no need to declare it since you can access protected procedures from the same unit. TMoveSG = class(TCustomGrid); does the trick having the protected MoveRow() in the same unit.
– bummi
Apr 30 '14 at 15:18
Thank you! But I still have the error (I use Lazarus). It says that 'identifier idents no member "MoveRow" '
– Alberto Rossi
Apr 30 '14 at 15:19
1
@AlbertoRossi taking a quick peek into Lazarus (I'm not familiar with)TMoveSG(StringGrid1).DoOPMoveColRow(false,1,3);
should do the trick here, since MoveRow is not implemented. You will have to verify ....
– bummi
Apr 30 '14 at 15:36
Note: MoveRow only moves one row. It will not SWAP the indicated rows.
– Rigel
Jun 11 '14 at 9:52
add a comment |
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.
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.
edited Nov 11 at 10:48
Kromster
4,77164886
4,77164886
answered Apr 29 '14 at 15:13
bummi
25k85289
25k85289
Wait but you didn't decalre TMoveSG(Grid).MoveRow(); anywhere
– Alberto Rossi
Apr 30 '14 at 15:13
@AlbertoRossi There is no need to declare it since you can access protected procedures from the same unit. TMoveSG = class(TCustomGrid); does the trick having the protected MoveRow() in the same unit.
– bummi
Apr 30 '14 at 15:18
Thank you! But I still have the error (I use Lazarus). It says that 'identifier idents no member "MoveRow" '
– Alberto Rossi
Apr 30 '14 at 15:19
1
@AlbertoRossi taking a quick peek into Lazarus (I'm not familiar with)TMoveSG(StringGrid1).DoOPMoveColRow(false,1,3);
should do the trick here, since MoveRow is not implemented. You will have to verify ....
– bummi
Apr 30 '14 at 15:36
Note: MoveRow only moves one row. It will not SWAP the indicated rows.
– Rigel
Jun 11 '14 at 9:52
add a comment |
Wait but you didn't decalre TMoveSG(Grid).MoveRow(); anywhere
– Alberto Rossi
Apr 30 '14 at 15:13
@AlbertoRossi There is no need to declare it since you can access protected procedures from the same unit. TMoveSG = class(TCustomGrid); does the trick having the protected MoveRow() in the same unit.
– bummi
Apr 30 '14 at 15:18
Thank you! But I still have the error (I use Lazarus). It says that 'identifier idents no member "MoveRow" '
– Alberto Rossi
Apr 30 '14 at 15:19
1
@AlbertoRossi taking a quick peek into Lazarus (I'm not familiar with)TMoveSG(StringGrid1).DoOPMoveColRow(false,1,3);
should do the trick here, since MoveRow is not implemented. You will have to verify ....
– bummi
Apr 30 '14 at 15:36
Note: MoveRow only moves one row. It will not SWAP the indicated rows.
– Rigel
Jun 11 '14 at 9:52
Wait but you didn't decalre TMoveSG(Grid).MoveRow(); anywhere
– Alberto Rossi
Apr 30 '14 at 15:13
Wait but you didn't decalre TMoveSG(Grid).MoveRow(); anywhere
– Alberto Rossi
Apr 30 '14 at 15:13
@AlbertoRossi There is no need to declare it since you can access protected procedures from the same unit. TMoveSG = class(TCustomGrid); does the trick having the protected MoveRow() in the same unit.
– bummi
Apr 30 '14 at 15:18
@AlbertoRossi There is no need to declare it since you can access protected procedures from the same unit. TMoveSG = class(TCustomGrid); does the trick having the protected MoveRow() in the same unit.
– bummi
Apr 30 '14 at 15:18
Thank you! But I still have the error (I use Lazarus). It says that 'identifier idents no member "MoveRow" '
– Alberto Rossi
Apr 30 '14 at 15:19
Thank you! But I still have the error (I use Lazarus). It says that 'identifier idents no member "MoveRow" '
– Alberto Rossi
Apr 30 '14 at 15:19
1
1
@AlbertoRossi taking a quick peek into Lazarus (I'm not familiar with)
TMoveSG(StringGrid1).DoOPMoveColRow(false,1,3);
should do the trick here, since MoveRow is not implemented. You will have to verify ....– bummi
Apr 30 '14 at 15:36
@AlbertoRossi taking a quick peek into Lazarus (I'm not familiar with)
TMoveSG(StringGrid1).DoOPMoveColRow(false,1,3);
should do the trick here, since MoveRow is not implemented. You will have to verify ....– bummi
Apr 30 '14 at 15:36
Note: MoveRow only moves one row. It will not SWAP the indicated rows.
– Rigel
Jun 11 '14 at 9:52
Note: MoveRow only moves one row. It will not SWAP the indicated rows.
– Rigel
Jun 11 '14 at 9:52
add a comment |
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;
add a comment |
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;
add a comment |
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;
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;
edited Apr 29 '14 at 15:45
answered Apr 29 '14 at 14:28
Re0sless
7,99444263
7,99444263
add a comment |
add a comment |
Are you using Lazarus instead of Delphi? I'm not aware of a sorting feature in the standard Delphi StringGrid implementation.
The issue is that the StringGrid is just that, a grid of Strings. It has no understanding that the strings are in fact numbers, so it can only sort them alphabetically.
If you are looking for a more complex sorting ability than the grid provides naturally you have a couple of choices. Either override the TStringGrid
class and produce your own specialised version that sorts how you want it to (i'm afraid i don't have the Lazarus source code to hand to give you more specific instructions), or you can simply sort the records before you put them into the grid.
For instance, if you have a TPlayer
class that represents your player and their scores then you can use TList.Sort(..)
to sort it however you like before you loop over and populate the grid.
//
// Sort the player scores by their *numeric* value, not by the string representation
//
function SortByPlayerScore(A, B: Pointer) : integer
begin
Result := TPlayer(A).Score - TPlayer(B).Score;
end;
// ....
list.Sort(SortByPlayerScore);
grid.RowCount := list.Count + 1;
for i := 0 to list.Count - 1 do begin
grid.Cells[0, i + 1] := list[i].Name;
grid.Cells[1, i + 1] := IntToStr(list[i].Score);
end;
add a comment |
Are you using Lazarus instead of Delphi? I'm not aware of a sorting feature in the standard Delphi StringGrid implementation.
The issue is that the StringGrid is just that, a grid of Strings. It has no understanding that the strings are in fact numbers, so it can only sort them alphabetically.
If you are looking for a more complex sorting ability than the grid provides naturally you have a couple of choices. Either override the TStringGrid
class and produce your own specialised version that sorts how you want it to (i'm afraid i don't have the Lazarus source code to hand to give you more specific instructions), or you can simply sort the records before you put them into the grid.
For instance, if you have a TPlayer
class that represents your player and their scores then you can use TList.Sort(..)
to sort it however you like before you loop over and populate the grid.
//
// Sort the player scores by their *numeric* value, not by the string representation
//
function SortByPlayerScore(A, B: Pointer) : integer
begin
Result := TPlayer(A).Score - TPlayer(B).Score;
end;
// ....
list.Sort(SortByPlayerScore);
grid.RowCount := list.Count + 1;
for i := 0 to list.Count - 1 do begin
grid.Cells[0, i + 1] := list[i].Name;
grid.Cells[1, i + 1] := IntToStr(list[i].Score);
end;
add a comment |
Are you using Lazarus instead of Delphi? I'm not aware of a sorting feature in the standard Delphi StringGrid implementation.
The issue is that the StringGrid is just that, a grid of Strings. It has no understanding that the strings are in fact numbers, so it can only sort them alphabetically.
If you are looking for a more complex sorting ability than the grid provides naturally you have a couple of choices. Either override the TStringGrid
class and produce your own specialised version that sorts how you want it to (i'm afraid i don't have the Lazarus source code to hand to give you more specific instructions), or you can simply sort the records before you put them into the grid.
For instance, if you have a TPlayer
class that represents your player and their scores then you can use TList.Sort(..)
to sort it however you like before you loop over and populate the grid.
//
// Sort the player scores by their *numeric* value, not by the string representation
//
function SortByPlayerScore(A, B: Pointer) : integer
begin
Result := TPlayer(A).Score - TPlayer(B).Score;
end;
// ....
list.Sort(SortByPlayerScore);
grid.RowCount := list.Count + 1;
for i := 0 to list.Count - 1 do begin
grid.Cells[0, i + 1] := list[i].Name;
grid.Cells[1, i + 1] := IntToStr(list[i].Score);
end;
Are you using Lazarus instead of Delphi? I'm not aware of a sorting feature in the standard Delphi StringGrid implementation.
The issue is that the StringGrid is just that, a grid of Strings. It has no understanding that the strings are in fact numbers, so it can only sort them alphabetically.
If you are looking for a more complex sorting ability than the grid provides naturally you have a couple of choices. Either override the TStringGrid
class and produce your own specialised version that sorts how you want it to (i'm afraid i don't have the Lazarus source code to hand to give you more specific instructions), or you can simply sort the records before you put them into the grid.
For instance, if you have a TPlayer
class that represents your player and their scores then you can use TList.Sort(..)
to sort it however you like before you loop over and populate the grid.
//
// Sort the player scores by their *numeric* value, not by the string representation
//
function SortByPlayerScore(A, B: Pointer) : integer
begin
Result := TPlayer(A).Score - TPlayer(B).Score;
end;
// ....
list.Sort(SortByPlayerScore);
grid.RowCount := list.Count + 1;
for i := 0 to list.Count - 1 do begin
grid.Cells[0, i + 1] := list[i].Name;
grid.Cells[1, i + 1] := IntToStr(list[i].Score);
end;
answered Apr 29 '14 at 14:18
Stik
168211
168211
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f23366062%2fsort-tstringgrid-by-row-and-its-integer-value%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown