Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort TStringGrid by row and its integer value

Tags:

sorting

delphi

I have StringGrid with 2 columns: Player and Scores. I have to sort this table looking at the score of a player.

enter image description here

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?

like image 457
Alberto Rossi Avatar asked Apr 29 '14 13:04

Alberto Rossi


2 Answers

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.

enter image description hereenter image description here

like image 200
bummi Avatar answered Nov 15 '22 05:11

bummi


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;
like image 34
Re0sless Avatar answered Nov 15 '22 03:11

Re0sless