Recent

Author Topic: [Solved] Sort TStringgrid, exclude empty cells but keep Grid.Objects  (Read 724 times)

Hansvb

  • Hero Member
  • *****
  • Posts: 799
Hi,

I would like to sort a TStringGrid. The empty cells should be ignored during sorting. This works with the following function:

Code: Pascal  [Select][+][-]
  1. function TfrmMain.SortGridByColumn(Grid : TStringGrid; Column : Integer;
  2.   Ascending : Boolean) : Boolean;
  3. var
  4.   i, j: Integer;
  5.   Temp: string;
  6. begin
  7.   Result := True;
  8.   try
  9.     for i := 0 to Grid.RowCount - 2 do
  10.       for j := i + 1 to Grid.RowCount - 1 do
  11.         if (Grid.Cells[Column, j] <> '') and ((Grid.Cells[Column, i] = '') or (CompareText(Grid.Cells[Column, i], Grid.Cells[Column, j]) > 0 = Ascending)) then
  12.         begin
  13.           Temp := Grid.Cells[Column, i];
  14.           Grid.Cells[Column, i] := Grid.Cells[Column, j];
  15.           Grid.Cells[Column, j] := Temp;
  16.         end;
  17.   except
  18.     Result := False;
  19.   end;
  20. end;  


I also have StringGrid.Objects[] They should be assigned to the correct cell again. See the function below, This crashes with an access violation.
I am probably not cleaning up my object in the right place? What am I missing?

Code: Pascal  [Select][+][-]
  1. function TfrmMain.SortGridByColumn(Grid : TStringGrid; Column : Integer;
  2.   Ascending : Boolean) : Boolean;
  3. var
  4.   i, j: Integer;
  5.   Temp: string;
  6.   DataObject: P_Item_ObjectData;
  7. begin
  8.   Result := True;
  9.   try
  10.     for i := 0 to Grid.RowCount - 2 do
  11.       for j := i + 1 to Grid.RowCount - 1 do
  12.         if (Grid.Cells[Column, j] <> '') and ((Grid.Cells[Column, i] = '') or (CompareText(Grid.Cells[Column, i], Grid.Cells[Column, j]) > 0 = Ascending)) then
  13.         begin
  14.           Temp := Grid.Cells[Column, i];
  15.           DataObject := P_Item_ObjectData(Grid.Objects[Column, j]); //
  16.  
  17.           if P_Item_ObjectData(Grid.Objects[Column, j]) <> nil then begin
  18. //            Dispose(P_Item_ObjectData(Grid.Objects[Column, j]));  // access violation in row 5 ?
  19. //            Grid.Objects[Column, j] := Nil;
  20.           end;
  21.  
  22.           if DataObject <> nil then begin
  23.             if P_Item_ObjectData(Grid.Objects[Column, j]) <> nil then begin
  24.               Dispose(P_Item_ObjectData(Grid.Objects[Column, j]));  // access violation in row 5 ?
  25.               Grid.Objects[Column, j] := Nil;
  26.             end;
  27.  
  28.             Grid.Objects[Column, j] := TObject(DataObject);
  29.           end;
  30.  
  31.           Grid.Cells[Column, i] := Grid.Cells[Column, j];
  32.           Grid.Cells[Column, j] := Temp;
  33.  
  34.         end;
  35.   except
  36.     Result := False;
  37.   end;
  38. end;

Or is there a standard sorting function in TSTringgrid which ignores empty cells an keeps track of Grid.Objects?
« Last Edit: April 13, 2025, 06:35:54 pm by Hansvb »

cdbc

  • Hero Member
  • *****
  • Posts: 2124
    • http://www.cdbc.dk
Re: Sort TStringgrid, exclude empty cells but keep Grid.Objects
« Reply #1 on: April 11, 2025, 04:05:08 pm »
Hi
In your first algorithm, I would probably make 'Temp' a
Code: Pascal  [Select][+][-]
  1. type
  2.   TCopyRec = record
  3.     crObj: TObject;
  4.     crStr: String;
  5.   end;

and then:
Code: Pascal  [Select][+][-]
  1. function TfrmMain.SortGridByColumn(Grid : TStringGrid; Column : Integer;
  2.   Ascending : Boolean) : Boolean;
  3. var
  4.   i, j: Integer;
  5.   Temp: TCopyRec;
  6. begin
  7.   Result := True;
  8.   try
  9.     for i := 0 to Grid.RowCount - 2 do
  10.       for j := i + 1 to Grid.RowCount - 1 do
  11.         if (Grid.Cells[Column, j] <> '') and ((Grid.Cells[Column, i] = '') or (CompareText(Grid.Cells[Column, i], Grid.Cells[Column, j]) > 0 = Ascending)) then
  12.         begin
  13.           Temp.crStr := Grid.Cells[Column, i];
  14.           Temp.crObj := Grid.Objects[Column, i];
  15.           Grid.Cells[Column, i] := Grid.Cells[Column, j];
  16.           Grid.Objects[Column, i] := Grid.Objects[Column, j];
  17.           Grid.Cells[Column, j] := Temp.crStr;
  18.           Grid.Objects[Column, j] := Temp.crObj;
  19.         end;
  20.   except
  21.     Result := False;
  22.   end;
  23. end;  
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 799
Re: Sort TStringgrid, exclude empty cells but keep Grid.Objects
« Reply #2 on: April 12, 2025, 09:03:04 am »
Thanks, that works.
There was another thinking error in the sort routine that I hadn't seen. I lost 1 value (cell content) of my stringgrid. That has now been adjusted as well. This is the new basis that I can continue with. (Leaving the record aside for now, it will probably go to an ExecNoRes if you know what I mean :) ).

Code: Pascal  [Select][+][-]
  1. function TfrmMain.SortGridByColumn(Grid: TStringGrid; Column: Integer;
  2.   Ascending: Boolean): Boolean;
  3. var
  4.   i, j: Integer;
  5.   TempStr: string;
  6.   TempObj: TObject;
  7.   StartRow: Integer;
  8. begin
  9.   Result := True;
  10.   try
  11.     StartRow := Grid.FixedRows; // Do not sort the header(s), skip them.
  12.  
  13.     for i := StartRow to Grid.RowCount - 2 do
  14.       for j := i + 1 to Grid.RowCount - 1 do
  15.       begin
  16.         if (Grid.Cells[Column, j] <> '') then
  17.         begin
  18.           if (Grid.Cells[Column, i] = '') or
  19.              (Ascending and (CompareText(Grid.Cells[Column, i], Grid.Cells[Column, j]) > 0)) or
  20.              (not Ascending and (CompareText(Grid.Cells[Column, i], Grid.Cells[Column, j]) < 0)) then  //  only Ascending for now as test.
  21.           begin
  22.             // Switch the cell contents includinng the "grid.objects".
  23.             TempStr := Grid.Cells[Column, i];
  24.             TempObj := Grid.Objects[Column, i];
  25.  
  26.             Grid.Cells[Column, i] := Grid.Cells[Column, j];
  27.             Grid.Objects[Column, i] := Grid.Objects[Column, j];
  28.  
  29.             Grid.Cells[Column, j] := TempStr;
  30.             Grid.Objects[Column, j] := TempObj;
  31.           end;
  32.         end;
  33.       end;
  34.   except
  35.     Result := False;
  36.   end;
  37. end;  

cdbc

  • Hero Member
  • *****
  • Posts: 2124
    • http://www.cdbc.dk
Re: Sort TStringgrid, exclude empty cells but keep Grid.Objects
« Reply #3 on: April 12, 2025, 10:01:42 am »
Hi Hans
Glad to hear it works  :) Yup - I know  ;)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Nicole

  • Hero Member
  • *****
  • Posts: 1095
Re: Sort TStringgrid, exclude empty cells but keep Grid.Objects
« Reply #4 on: April 12, 2025, 04:07:30 pm »
Not sure, if there is a setting for ignoring empty lines there.
However there exists a component names alike "Dynamic String Grid" of Tony, which allows the sorting by the click of the mouse on all columns as in Excel.

wp

  • Hero Member
  • *****
  • Posts: 12784
Re: Sort TStringgrid, exclude empty cells but keep Grid.Objects
« Reply #5 on: April 12, 2025, 11:28:11 pm »
I am confused by the name of your procedure: "SortGridByColumn" implies for me that the entire grid is sorted so that the cells in the given column are ordered; I would expect that complete rows are exchanged so that each cell in each row keeps its horizontal neighbour. (This is already available in TStringGrid as method SortColRow(IsColumn: Boolean; index:Integer).

Your code, however, sorts only the cells in the given column, all other cells are not touched. If this really is your intention you could do this alternatively by copying the column to be sorted into a TStringList which has sorting capabilities. You'll have to provide a custom compare function because you want to have blank cells at the end of the column after sorting.

The attached project demonstrates all cases:
- Button1 triggers your function "SortGridByColumn".
- Button2 triggers TStringGrid.SortByColRow and sorts the entire grid
- Button3 uses the auxiliary TStringList for sorting.

All the non-blank cells in this demo grid have an object attached; it is the same integer shown as cell text cast to a pointer. When you click into the grid the Cells[] text and the Objects[] are read from the clicked cell and displayed by two labels - both labels should always display the same. Note that all sorting methods tested keep Cells[] and Objects[] together. In case of SortByColRow this is because the grid stores Cells[] and Objects[] as elements of the same internal record (TCellProps) and thus stay together automatically when the cell records are rearranged during sorting. In case of the aux stringlist, the Assign method also copies the pointers along with the texts into the destination list.

Hansvb

  • Hero Member
  • *****
  • Posts: 799
Re: Sort TStringgrid, exclude empty cells but keep Grid.Objects
« Reply #6 on: April 13, 2025, 09:32:08 am »
Quote
sorts only the cells in the given column, all other cells are not touched. If this really is your intention you could do this alternatively by copying the column to be sorted into a TStringList

It is indeed my intention to sort only 1 column. I had not thought of possibly copying it to a TStringlist. I will save your example, if I still want to sort all rows and exclude the empty cells, I can use that well.

 

TinyPortal © 2005-2018