erva 2 Posted May 8, 2020 Simple question: How to sort values in TStringGrid based on one columns values? Searched with google and find only articles that are 6-8 years old. Is TStringGrid evolved from those days or are info in these still relevant? I mean is there any easier solution for sorting than these old articles? Share this post Link to post
Attila Kovacs 629 Posted May 8, 2020 I don't know FMX, but normally a StringGrid is just a display and you sort the underlying data. But I don't know FMX. Share this post Link to post
Remy Lebeau 1397 Posted May 9, 2020 Hard to answer without knowing which articles you are actually reading. But most likely, the way to sort a grid in FMX has likely not changed much, if at all. Share this post Link to post
erva 2 Posted May 9, 2020 Haven't used Delphi for a while, 15 years, and have managed to forget things little. But just remember based on @Attila Kovacsanwer that IndexDefFields is answer. Already managed to sort records ascending order, now have to still find out how to get them descending order. Using RemObjects Data Abstract. Adding to IndexDefField "Column1:D" didn't work. Now finally managed to get records in descending order creating index at IndexDef and setting IndexName property to created index. But how to refresh index when changing records at StringGrid. Calling table.Close and after that table.Open methods didn't change records order. Tryed table.Refresh also but with no help. I'am using RemObjects DA but i guess there's properties and methods like in other TDataSet related stuff like FireDAC. Share this post Link to post
Guest Posted May 30, 2020 For TStringGrid once I have used merge sort implemented as an extension (it was VCL version of TStringGrid), if that can help, here it is: procedure TStringGrid.MSort(const SortCol: integer; const DataType: TDataType; const Ascending: boolean); begin var List: TArray<integer>; var TempGrid: TStringGrid:=TStringGrid.create(nil); try TempGrid.RowCount :=RowCount; TempGrid.ColCount :=ColCount; TempGrid.FixedRows:=FixedRows; SetLength(List, RowCount - FixedRows); for var Index: integer:=FixedRows to RowCount - 1 do begin List[Index - FixedRows]:=Index; TempGrid.Rows[Index].Assign(Rows[Index]); end; TSorting.MergeSort(Self, List, SortCol, DataType, Ascending); for var Index: integer:=0 to RowCount - FixedRows - 1 do begin Rows[Index + FixedRows].Assign(TempGrid.Rows[List[Index]]); end; Row:=FixedRows; finally TempGrid.Free; end; SetLength(List, 0); end; And TSorting class contained with Quick Sort and Merge Sort, The Merge Sort part: class procedure TSorting.MergeSort(Grid: TStringGrid; var Vals: array of integer; sortcol: integer; datatype: TDataType; ascending: boolean); // Temporary shared local array for integers. var Avals: array of integer; // Helper nested method for comparision. function compare(val1, val2: string): integer; begin case datatype of TDataType.TString: result:=ANSIComparetext(val1, val2); TDataType.TInteger: begin var int1: int64:=strtointdef(val1, 0); var int2: int64:=strtointdef(val2, 0); if (int1 > int2) then result:= 1 else if int1 < int2 then result:= -1 else result:=0; end; TDataType.TFloat: begin var errcode: integer; var float1: extended; var float2: extended; val(val1, float1, errcode); if errcode <> 0 then float1:=0; val(val2, float2, errcode); if errcode <> 0 then float2:=0; if float1 > float2 then result:= 1 else if float1 < float2 then result:= -1 else result:=0; end; else result:=0; end; end; // Heper nested merge method. procedure Merge(ALo, AMid, AHi: integer); begin var j: integer; var k: integer; var m: integer; var n: integer; var i: integer:=0; // Copy lower half of "vals" into temporary array "avals" SetLength(Avals, AMid - ALo + 1); for j:=ALo to AMid do begin AVals[i]:=Vals[j]; inc(i); end; // Initialize i:=0; j:=AMid + 1; k:=ALo; // ---------------------------------------------------------------------------- // Compare upper half to copied version of the lower half and move // the appropriate value (smallest for ascending, largest for descending) into // the lower half positions, for equals use 'avals' to preserve original order. // ---------------------------------------------------------------------------- // Execute moving while ((k < j) and (j <= AHi)) do begin with grid do n:=compare(Cells[sortcol, Vals[j]], Cells[sortcol, AVals[i]]); if ascending and (n >= 0) or ((not ascending) and (n <= 0)) then begin Vals[k]:=AVals[i]; inc(i); inc(k); end else begin Vals[k]:=Vals[j]; inc(k); inc(j); end; end; // Copy any remaning, unsorted elements for m:=k to j - 1 do begin Vals[m]:=AVals[i]; inc(i); end; end; // Recursively split the value into two pieces and merge them back together as we unwind the recursion. procedure PerformMergeSort(ALo, AHi:Integer); begin var AMid:Integer; if (ALo < AHi) then begin AMid:=(ALo + AHi) shr 1; PerformMergeSort(ALo, AMid); PerformMergeSort(AMid + 1, AHi); Merge(ALo, AMid, AHi); end; end; begin PerformMergeSort(0, high(Vals)); end; Then the usage was simply on any string grid component, just call MSort with desired parameters and done 🙂 Share this post Link to post
David Heffernan 2345 Posted May 30, 2020 Writing a sorting algorithm into a UI control is a bad idea. Keep the two separate. You'd only really need to use merge sort if you needed a stable sort. And then you'd have to take care that the merge sort algo was a stable one. Surely there is perfectly usable built in sorting code? Share this post Link to post
Guest Posted May 30, 2020 (edited) 1 hour ago, David Heffernan said: Writing a sorting algorithm into a UI control is a bad idea. Keep the two separate. You'd only really need to use merge sort if you needed a stable sort. And then you'd have to take care that the merge sort algo was a stable one. Hmm... look again, sorting algorithm is in separate class already and it is not part of the UI (class procedure TSorting.MergeSort) 🙂 The component however is extended to allow easy usage, but I agree you can change the way you apply sorting to the control, I'm too lazy to do that, this is just an example 😉 . Edited May 30, 2020 by Guest Share this post Link to post
David Heffernan 2345 Posted May 30, 2020 OK, I was sloppy. I still don't get why you wrote this sorting function rather than use built in algos. Share this post Link to post
Guest Posted May 30, 2020 56 minutes ago, David Heffernan said: OK, I was sloppy. I still don't get why you wrote this sorting function rather than use built in algos. Good question! I believe I was not aware of any built in algos other that TStringList.Sort method at the time and that code came from pure VCL project with no 3rd party libraries or other frameworks. But of course you've got the point on built in solutions. Share this post Link to post
Harry Stahl 1 Posted May 31, 2020 FMX has no build in option for sorting grids (like TMS-FMX-Grid). FMX StringGrid should be used for displaying and selecting (smaller portions of) data. If you have more data to display you should consider to use The TGRID. Because this grid does not store the data itself, it displays the data only (in the Event "OnGetValue" you are asked to deliver the relevant data). In this case a new sorting of the whole grid is easier to manage, because you have not to fill the whole grid again with data (or make a new sorting with tricks like above). Only the little part that is displayed will get an update. At least only your data needs to be sorted (e.g. by a field, that represents names, dates or numbers) and you display it in the (T)Grid. Share this post Link to post