Sort TStringGrid by row and its integer value












4














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?










share|improve this question



























    4














    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?










    share|improve this question

























      4












      4








      4


      1





      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?










      share|improve this question













      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?







      delphi sorting






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Apr 29 '14 at 13:27









      Alberto Rossi

      86622048




      86622048
























          3 Answers
          3






          active

          oldest

          votes


















          5














          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






          share|improve this answer























          • 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



















          2














          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;





          share|improve this answer































            1














            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;





            share|improve this answer





















              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
              });


              }
              });














              draft saved

              draft discarded


















              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









              5














              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






              share|improve this answer























              • 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
















              5














              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






              share|improve this answer























              • 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














              5












              5








              5






              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






              share|improve this answer














              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







              share|improve this answer














              share|improve this answer



              share|improve this answer








              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


















              • 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













              2














              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;





              share|improve this answer




























                2














                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;





                share|improve this answer


























                  2












                  2








                  2






                  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;





                  share|improve this answer














                  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;






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Apr 29 '14 at 15:45

























                  answered Apr 29 '14 at 14:28









                  Re0sless

                  7,99444263




                  7,99444263























                      1














                      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;





                      share|improve this answer


























                        1














                        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;





                        share|improve this answer
























                          1












                          1








                          1






                          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;





                          share|improve this answer












                          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;






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Apr 29 '14 at 14:18









                          Stik

                          168211




                          168211






























                              draft saved

                              draft discarded




















































                              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.




                              draft saved


                              draft discarded














                              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





















































                              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







                              這個網誌中的熱門文章

                              Xamarin.form Move up view when keyboard appear

                              Post-Redirect-Get with Spring WebFlux and Thymeleaf

                              Anylogic : not able to use stopDelay()