![]() |
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... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:15 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