Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi StringGrid sortieren (https://www.delphipraxis.net/91439-stringgrid-sortieren.html)

yankee 4. Mai 2007 22:20


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:
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;
Die Funktion sortiert nicht nur Spalten mit Zahlen und Zeichen, sondern auch Spalten mit gemischten Werten.
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?

BenjaminH 6. Jul 2007 15:51

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:
9d
9a
9c
9b
Nach
Delphi-Quellcode:
if n1 > n2 then
begin
  result :=1;
  exit
end
else if n1 < n2 then
begin
  result :=-1;
  exit;
end
hab ich noch das eingefügt:
Delphi-Quellcode:
else
begin
  Dec(c1);
  Dec(c2);
end;

yankee 7. Jul 2007 09:55

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.

_frank_ 7. Jul 2007 12:53

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

yankee 7. Jul 2007 13:38

Re: StringGrid sortieren
 
Jetzt nochmal die CompareFunktion mit Sortierrichtung:
Delphi-Quellcode:
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;
Das blöde ist: wieder eine globale Variable mehr

EDIT: Position von dem inc(c1)/c2 korrigiert

_frank_ 7. Jul 2007 23:43

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

Real_Thunder 3. Nov 2007 07:17

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

yankee 3. Nov 2007 09:33

Re: StringGrid sortieren
 
Zitat:

Zitat von Real_Thunder
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 ?

In der SortStringGrid-Funktion könntest du beim einfügen der Pointer in die TList den Pointer speichern, der auf deine markierte Zeile passt. Und dann noch in einem int einfach die wievielte Spalte markiert war (das ändert sich ja nicht).
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.

NetSonic 5. Nov 2008 14:14

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:
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;
Gruß, NetSonic

taaktaak 5. Nov 2008 14:26

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?!) Testanwendung

// 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 22:39 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz