![]() |
StringGrid sortieren
Hi @ll,
ich habe Ende 2004 schonmal eine procedure dafür in der CodeLib geposted. Da ich gerade eine Frage dazu bekam bin ich mal wieder auf diese aufmerksam geworden und habe mir überlegt, dass ich das besser kann und nochmal gerne eine schnellere und besser vorschlagen würde:
Delphi-Quellcode:
Die Funktion sortiert nicht nur Spalten mit Zahlen und Zeichen, sondern auch Spalten mit gemischten Werten.
uses Grids,Classes;
var col: integer; function CompareStringGridRows(item1,item2: Pointer):integer; var c1,c2: ^char; n1,n2: integer; begin c1 :=@TStrings(item1).Strings[col][1]; c2 :=@TStrings(item2).Strings[col][1]; while (c1^ <>#0) and (c2^ <>#0) do begin if ((c1^ in ['0'..'9']) and (c2^ in ['0'..'9'])) then begin n1 :=0; n2 :=0; while (c1^ in ['0'..'9']) do begin n1 :=n1*10+ord(c1^)-ord('0'); inc(c1); end; while (c2^ in ['0'..'9']) do begin n2 :=n2*10+ord(c2^)-ord('0'); inc(c2); end; if n1 > n2 then begin result :=1; exit end else if n1 < n2 then begin result :=-1; exit; end; end else //at least one is not a number begin if c1^ > c2^ then begin result :=1; exit; end else if c1^ < c2^ then begin result :=-1; exit; end; end; inc(c1); inc(c2); end; if c1^ =#0 then begin if c2^ =#0 then result :=0 else result :=-1; end else result :=1; end; procedure SortStringGrid(thegrid: TStringGrid; const col: integer); var rows: TList; i: integer; tmp: TStrings; begin rows :=TList.Create; rows.Capacity :=thegrid.RowCount; for i:=0 to thegrid.RowCount -1 do begin tmp :=TStringList.Create; tmp.AddStrings(thegrid.Rows[i]); thegrid.Rows[i].Clear; rows.Add(tmp); end; rows.Sort(CompareStringGridRows); for i:=0 to thegrid.RowCount -1 do begin thegrid.Rows[i].Assign(rows[i]); end; rows.Free; end; Soweit ich getested habe funktioniert die Funktion auch, hat aber 1 dicken Nachteil: Sie braucht die globale Variable col für die Vergleichsunktion. Komme ich irgendwie drumrum mir 'ne eigene Sortierfunktion zu schreiben und kann dabei trotzdem eine globale Variable verwenden? Und was genau macht Assign()? Kann es hierbei zu Speicherlecks führen? Also gibt Assign das alte Objekt frei oder wie soll ich mir das generell vorstellen? |
Re: StringGrid sortieren
Ich hab die Sortierfunktion jetzt bei mir auch im Einsatz. Aber um etwas ergänzt, damit sie auch diese Einträge sortieren kann:
Code:
Nach
9d
9a 9c 9b
Delphi-Quellcode:
hab ich noch das eingefügt:
if n1 > n2 then
begin result :=1; exit end else if n1 < n2 then begin result :=-1; exit; end
Delphi-Quellcode:
else
begin Dec(c1); Dec(c2); end; |
Re: StringGrid sortieren
@BenjaminH:
Ich habe zwar lange gebraucht, bis ich das Problem verstanden habe, aber ich glaube jetzt ist es mir klar. Nur ist deine Lösung so nicht "richtig"... Es wäre besser/schöner das inc(c1)/inc(c2) in Zeile 50 noch in den direkt darüber liegenden else-Block zu verschieben. *meinen Post oben editier* EDIT: toll... mit Editieren ist Pustekuchen. |
Re: StringGrid sortieren
Liste der Anhänge anzeigen (Anzahl: 1)
hat das evtl. schon jemand mit sortierrichtungen implementiert?
ich hab das mal versucht, leider funktioniert das nicht so wirklich...wird trotzdem normal (aufsteigend) sortiert. der Debugger sagt mir in der Sortier-Procedure immer result ist 0 :gruebel: , obwohl ja offensichtlich sortiert wird... ich hab mal das komplette test-programm angehängt... Gruß Frank |
Re: StringGrid sortieren
Jetzt nochmal die CompareFunktion mit Sortierrichtung:
Delphi-Quellcode:
Das blöde ist: wieder eine globale Variable mehr
var col,dir: integer; //aufsteigend: dir=1, absteigend: dir=-1
function CompareStringGridRows(item1,item2: Pointer):integer; var c1,c2: ^char; n1,n2: integer; begin c1 :=@TStrings(item1).Strings[col][1]; c2 :=@TStrings(item2).Strings[col][1]; while (c1^ <>#0) and (c2^ <>#0) do begin if ((c1^ in ['0'..'9']) and (c2^ in ['0'..'9'])) then begin n1 :=0; n2 :=0; while (c1^ in ['0'..'9']) do begin n1 :=n1*10+ord(c1^)-ord('0'); inc(c1); end; while (c2^ in ['0'..'9']) do begin n2 :=n2*10+ord(c2^)-ord('0'); inc(c2); end; if n1 > n2 then begin result :=dir; exit end else if n1 < n2 then begin result :=dir*-1; exit; end; end else //at least one is not a number begin if c1^ > c2^ then begin result :=dir; exit; end else if c1^ < c2^ then begin result :=dir*-1; exit; end; inc(c1); inc(c2); end; end; if c1^ =#0 then begin if c2^ =#0 then result :=0 else result :=dir*-1; end else result :=dir; end; EDIT: Position von dem inc(c1)/c2 korrigiert |
Re: StringGrid sortieren
Liste der Anhänge anzeigen (Anzahl: 1)
danke yankee,
hab das ganze mal noch um evtl. angehängten Objekte erweitert und das Speicherleck entfernt (temporäre Stringlisten werden erzeugt aber nicht freigegeben). sollte zumindest eins sein, habs jetzt nicht mit fastmm probiert, aber es kommt keine Exception beim free...
Delphi-Quellcode:
wenn ich jetzt keinen Schusselfehler gemacht habe, sollte es so hinhauen (kann das mit den Objekten jetzt nicht so auf die Schnelle testen). Teste das spätestens, wenn ichs in den DFM-Editor einbaue :)
procedure SortStringGrid(thegrid: TStringGrid; const col: integer);
var rows: TList; i,j: integer; tmp: TStrings; begin rows :=TList.Create; rows.Capacity :=thegrid.RowCount-thegrid.fixedrows; for i:=theGrid.fixedrows to thegrid.RowCount -1 do begin tmp :=TStringList.Create; for j:=0 to thegrid.ColCount-1 do tmp.AddObject(thegrid.cells[j,i],thegrid.Objects[j,i]); //tmp.AddStrings(thegrid.Rows[i]); thegrid.Rows[i].Clear; rows.Add(tmp); end; rows.Sort(CompareStringGridRows); for i:=thegrid.fixedrows to thegrid.RowCount -1 do begin //thegrid.Rows[i].Assign(rows[i-thegrid.fixedrows]); for j:=0 to thegrid.ColCount-1 do begin thegrid.Cells[j,i]:=TStringlist(rows[i-thegrid.fixedrows]).Strings[j]; thegrid.Objects[j,i]:=TStringlist(rows[i-thegrid.fixedrows]).Objects[j]; end; TStringlist(rows[i-thegrid.fixedrows]).free; end; rows.Free; end; baue mir erstmal noch eine Routine, um einen Pfeil auf die entsprechende Spalte zu malen... //edit: das komplette Testproggy inkl. Ownerdraw und Pfeil (geht vielleicht noch schöner :) ) im Anhang für interessierte Gruß Frank |
Re: StringGrid sortieren
Ich hätte noch eine Frage, und zwar möchte ich das die makierte Zelle vom Stringgrid,
auch nach der Sortierung makiert bleibt. (an anderer stelle). Ist es irgendwie möglich ? oder geht es nur mit nem eindeutigem identifer... quasi eine zelle mit breite -1 in demr man dan die anfangsreinfolge speichert, und nach dem sortiervorgang in dieser spalte den "identifer" sucht. ich stelle mir diese art der lösung sehr schmutzig und langsam vor..... Wenn jemand eine elegantere lösung hat, bitte melden. MfG Real Thunder |
Re: StringGrid sortieren
Zitat:
Nach dem Sortieren musst du dann nurnoch den Pointer in der TList wiederfinden (TList hat dafür glaube ich sogar eine fertige Methode) und schon hast du auch die neue Zeile deiner markierten Zelle. |
Re: StringGrid sortieren
Ich muss diesen - doch schön älteren Thread - mal eben wieder ausgraben.
Möchte meine StringGrid mit dieser Funktion sortieren, das klappt auch ohne Probleme bei Integer-Werten etc.. Allerdings enthalten die zu sortieren Spalten ein Datum als String im Format TT.MM.JJJJ und dies möchte ich korrekt sortieren. Leider bekomme ich nicht den richtigen Dreh, die Funktion so umzuschreiben, dass das auch wirklich funktioniert. Hat da jemand einen Denkanstoss für mich oder sowas schon gemacht und kann mir da helfen?
Delphi-Quellcode:
Gruß, NetSonic
var col,dir: integer;
function CompareStringGridRows(item1,item2: Pointer):integer; var c1,c2: ^char; n1,n2: integer; begin c1 :=@TStrings(item1).Strings[col][1]; c2 :=@TStrings(item2).Strings[col][1]; while (c1^ <>#0) and (c2^ <>#0) do begin if ((c1^ in ['0'..'9']) and (c2^ in ['0'..'9'])) then begin n1 :=0; n2 :=0; while (c1^ in ['0'..'9']) do begin n1 :=n1*10+ord(c1^)-ord('0'); inc(c1); end; while (c2^ in ['0'..'9']) do begin n2 :=n2*10+ord(c2^)-ord('0'); inc(c2); end; if n1 > n2 then begin result :=dir; exit end else if n1 < n2 then begin result :=dir*-1; exit; end; end else //at least one is not a number begin if c1^ > c2^ then begin result :=dir; exit; end else if c1^ < c2^ then begin result :=dir*-1; exit; end; inc(c1); inc(c2); end; end; if c1^ =#0 then begin if c2^ =#0 then result :=0 else result :=dir*-1; end else result :=dir; end; procedure SortStringGrid(thegrid: TStringGrid; const col: integer); var rows: TList; i,j: integer; tmp: TStrings; begin rows :=TList.Create; rows.Capacity :=thegrid.RowCount-thegrid.fixedrows; for i:=theGrid.fixedrows to thegrid.RowCount -1 do begin tmp :=TStringList.Create; for j:=0 to thegrid.ColCount-1 do tmp.AddObject(thegrid.cells[j,i],thegrid.Objects[j,i]); //tmp.AddStrings(thegrid.Rows[i]); thegrid.Rows[i].Clear; rows.Add(tmp); end; rows.Sort(CompareStringGridRows); for i:=thegrid.fixedrows to thegrid.RowCount -1 do begin //thegrid.Rows[i].Assign(rows[i-thegrid.fixedrows]); for j:=0 to thegrid.ColCount-1 do begin thegrid.Cells[j,i]:=TStringlist(rows[i-thegrid.fixedrows]).Strings[j]; thegrid.Objects[j,i]:=TStringlist(rows[i-thegrid.fixedrows]).Objects[j]; end; TStringlist(rows[i-thegrid.fixedrows]).free; end; rows.Free; end; |
Re: StringGrid sortieren
Mein Rat:
Wenn's auch bei einigen tausend Daten noch schnell sein soll: Vergiss es! Der Ansatz ist so weit verbreitet wie falsch! Halte die Daten in einer separaten Liste (z.B. TList) und nutze das Grid nur zur Anzeige. Alles andere ist nur für ein paar hundert Daten gut... // edit1 - Hierzu eine (überzeugende?!) ![]() // edit2 - Du musst eine spezielle Vergleichsprozedur CompareStringGridRows() schreiben, die zwei Zeilen unter Berücksichtigung des Datums vergleicht, d.h. Reihenfolge von tag.monat.jahr umdrehen... |
Re: StringGrid sortieren
Mehr sind es ja auch nicht - höchstens 200 Zeilen und dafür würde es doch reichen!? Aber mir fehlt halt nur der Ansatz, wie ich jetzt die Daten sortieren kann...
|
Re: StringGrid sortieren
Hier ien kleiner denkanstoß, es muß0 nciht das im grind drinsttehen weas er anzeugt, zauberwort ist oncelldraw.
Ich habe bei einer älteren anwendung zum beispiel das datum in ein unix timestamp verwandelt und in das grid geschrieben.. und angezeigt wird dann das datum. so läßt es sich leichter sortieren. Aber wie oben bereits erwähnt.. es ist nicht "die schnellste methode so zu sortieren" |
Re: StringGrid sortieren
Ah *klingelling* - ich glaube damit bringst Du mich auf die richtige Idee... :idea: Mal sehen, werde Deinen Ansatz mal verfolgen! Danke.
NetSonic |
Re: StringGrid sortieren
Zitat:
Delphi-Quellcode:
ungetestet und alles. Die Funkion substr gibt es in Delphi glaube ich nicht. D musst du glaube ich copy nehmen. Ich denke, du kannst dir denken, wie das gedacht ist...
var col,dir: integer;
function CompareStringGridRows(item1,item2: Pointer):integer; var s1,s2: string; n1,n2: integer; begin s1 :=@TStrings(item1).Strings[col]; s2 :=@TStrings(item2).Strings[col]; // Jahr n1 :=strtoint(substr(s1, -4, 4)); n2 :=strtoint(substr(s2, -4, 4)); if n1 < n2 then begin result :=dir; exit; else if n1 > n2 then begin result :=dir*-1; exit; end; //Monat: n1 :=strtoint(substr(s1, 4, 2)); n2 :=strtoint(substr(s2, 4, 2)); if n1 < n2 then begin result :=dir; exit; else if n1 > n2 then begin result :=dir*-1; exit; end; //Tag: n1 :=strtoint(substr(s1, 0, 2)); n2 :=strtoint(substr(s2, 0, 2)); if n1 < n2 then begin result :=dir; exit; else if n1 > n2 then begin result :=dir*-1; exit; end; result :=0; end; |
AW: StringGrid sortieren
Hallo Leute,
ich kram ja eigentlich nie was wieder aus, aber falls jemand mal darauf stößt : Seit neuem wird ja in Delphi auch mit Template-Klassen gearbeitet, und jeder der eine größere Liste von Daten hat, benutzt bestimmt auch TList<TYPE> aus den Generics.Collections. Wer die gerne sein StringGrid sortieren will, sollte wie schon bereits gesagt, doch seine Daten im Ursprung in einer solchen Liste womöglich aus Records halten und dort drin sortieren. Dies realisiert man mit einem TCustomComparer<TYPE> oder einer anderen Klasse vom Interface IComparer<TYPE>, sozusagen einer Klasse mit der function COMPARE(Left, Right : TYPE), die zwei Datensätze von eurem Listendatentyp vergleicht. Wollt ihr also eine Liste aus Records wie z.B.
Delphi-Quellcode:
sortieren, müsste eurer TTestRecComparer so aussehen :
TTestRec = record
sName : string; sDescr : string; dtTimestamp : TDateTime; end;
Delphi-Quellcode:
in Compare muss dan bloß Links mit rechts verglichen werden, wobei 0 gleich, etwas negativs links "größer", etwas positives rechts "größer" bedeutet.
TTestRecComparer = class(TCustomComparer<TTestRec>)
... function Compare(Left, Right : TTestRec) : integer; ... end; So würdet ihr aufsteigend sortieren, wollt ihr es jedoch absteigend, so müsste dem Comparer noch eine Info übergeben werden und dann braucht ihr bloß das normale Vergleichsergebniss invertieren, etwa so :
Delphi-Quellcode:
Wenn man nun seinen eigenen Comparer nutzen will, übergibt man einfach :
if soSortOrder = soDescending then
result := -result;
Delphi-Quellcode:
Ich hoffe das hilft euch beim Listen sortieren weiter, die ihr dann z.B. ins StringGrid übertragen wollt.
lcMyComparer := TTestRecComparer.Create(soASCENDING);
MyList.Sort(lcMyComparer); PS : ![]() ![]() ![]() ...könnten euch z.B. helfen EDIT : DateTime Werte sind ja Fließkommawerte und lassen sich also mit > , = und < vergleichen. Mit freundlichen Grüßen KFAF SP :) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:10 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz