TObjectList sortieren

Für Fragen von Einsteigern und Programmieranfängern...
Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

TObjectList sortieren

Beitrag von photor »

Hallo Forum,

ich brauche mal eure Hilfe; wahrscheinlich ist es ein einfaches Problem - deshalb im Einsteigerbereich. Ich habe auch schon im Forum gesucht und diesen Thread gefunden:
viewtopic.php?f=55&t=14512, komme es aber nicht hin.

Das Problem ist, dass ich 3 Listen in txt-Files einlese, die jeweils eine Knotennummer und einen Spannungswert enthalten, die zusammengeführt werden sollen. Dämlicherweise sind die Listen nicht nach Knotennummern sortiert (sondern nach dem Wert - so fällt es aus dem Programm raus).

Mein Gedanke war, die Listen jeweils in einer TObjectList<TStressEntry> zu speichern, wobei der TStressEntry definiert ist als "Pärchen" aus KnotenID und Spannungswert:

Code: Alles auswählen

type

  TStressEntry = class
    ID: integer;
    stress: TMyReal;
  end;

  TStressList = specialize TObjectList<TStressEntry>;

Das Einlesen der drei Listen aus den Text-Files funktioniert; Ergebnis sind 3 StressList-en. Jetzt müssten die nach der Knotennummer ID sortiert werden, so dass die jeweiligen Spannungswerte zum selben Knoten an der selben Position in den Listen stehen (und zusammen geführt werden können.

Im oben genannten Link wurde vorgeschlagen, eine Funktion Compare zu definieren:

Code: Alles auswählen

function CompareStressList(Item1, Item2: Pointer): integer;
Der Aufruf zum Sortieren sollte dann etwa so aussehen:

Code: Alles auswählen

  // sort the ObjectLists for NodeID
  StressList_Min.Sort(@CompareStressList);
  StressList_Int.Sort(@CompareStressList);
  StressList_Max.Sort(@CompareStressList); 
(Anschließend dann über die Listen iterieren und aus Knotennummer und den 3 Spannungswerten eine Struktur bauen).

Allerdings gelingt es mir nicht, in der Compare-Funktion, auf die ID zuzugreifen (und so zu vergleichen - ich würde einfach die IDs voneinander abziehen wollen). Irgendwie komme ich mit den Pointern und den Elementen der Entries durcheinander.

Wie müsste diese Vergleichsfunktion denn Funktion aussehen?

Danke für jeden Hinweis.
Ciao,
Photor


PS: ich hatte auch schon an ein TDictionary mit dem Schlüssel KnotenID gedacht. Es scheint aber so zu sein, dass nicht erkannt wird, wenn die Knotennummer schon als Key vorhanden ist, wenn schon eine Liste eingelesen wurde - so fällt die Zuordnung dann schwer. Dieser Ansatz scheint mir aber auch zu sehr "durch die Brust ins Auge" zu sein.

ArchChem
Beiträge: 83
Registriert: Mo 11. Jul 2022, 10:41

Re: TObjectList sortieren

Beitrag von ArchChem »

Hallo Photor,

wenn die Knotennummer die ID ist, dann könnte deine Vergleichsfunktion in etwa so aussehen:

Code: Alles auswählen

function CompareStressList(item1: TStressEntry; item2: TStressEntry): integer;
begin
  // Definiere, was passiert, falls einer der Vergleichsparameter nil ist.
  if (item1.id = nil) and (item2.id = nil) then exit(0);
  if item1.id = nil then exit(-1);
  if item2.id = nil then exit(1);
  
  // Der normale Vergleich
  if item1.id < item2.id then Result := -1
  else if item1.id = item2.id then Result := 0
  else if item1.id > item2.id then Result := 1;
end;
Diese Funktion kommt irgendwo in deine Unit, aber außerhalb einer Klasse. Und dann weißt du sie den Listen als Parameter zu (am besten direkt nachdem du die Listen erstellt hast):

Code: Alles auswählen

StressList_Min.Sort(@CompareStressList);
StressList_Int.Sort(@CompareStressList);
StressList_Max.Sort(@CompareStressList); 
Die ObjectList ruft die Funktion dann auf und übergibt die Vergleichsobjekte als item1 und item2. Der Zugriff innerhalb der Funktion erfolgt also ganz gewöhnlich über die Parameter. Pointer brauchst du nur für die Übergabe der Funktion an sich.

Ich hoffe, ich konnte dir helfen!

wennerer
Beiträge: 524
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: TObjectList sortieren

Beitrag von wennerer »

Hallo Photor,
wenn ich es richtig verstehe möchtest du doch eigentlich nur drei Textdateien vereinen, sortieren und wieder ausgeben.
Also ich würde die drei Textdateien einfach in eine Stringlist lesen und dann mit einer for Schleife die Stringlist durchlaufen. Jeden String würde ich mit einer Stringoperation so zerlegen das ich die ID habe (zum Bsp.: steht die ID vorn dann bis zum Leerzeichen, steht sie hinten ab Leerzeichen bis Ende) und danach sortieren.
Vielleicht stelle ich mir das aber auch zu einfach vor oder hab es falsch verstanden.

Viele Grüße
Bernd

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: TObjectList sortieren

Beitrag von af0815 »

Ich würde eine nach ID sortierte Liste machen da ev. eine Hashliste und einfach Zeile für Zeile aus der jeweiligen Datei lesen bzw. das zuerst in eine Stringliste einlesen und das ganze aus dieser Liste exportieren. Damit kann man auch leichter damit umgehen, falls eine ID in einer Liste fehlt, bzw die Listen nicht ganz gleich sind. Ausserdem ist es dann egal, ob du eine oder fünfzig Listen einliest.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1436
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: TObjectList sortieren

Beitrag von fliegermichl »

ArchChem hat geschrieben:
Di 12. Mär 2024, 20:56
...

Code: Alles auswählen

function CompareStressList(item1: TStressEntry; item2: TStressEntry): integer;
begin
  // Definiere, was passiert, falls einer der Vergleichsparameter nil ist.
  if (item1.id = nil) and (item2.id = nil) then exit(0);
  if item1.id = nil then exit(-1);
  if item2.id = nil then exit(1);
...  
Ich denke, der Vergleich ist falsch herum. Wenn Item1.id = nil, dann ist Item2 größer und umgekehrt.

wp_xyz
Beiträge: 4893
Registriert: Fr 8. Apr 2011, 09:01

Re: TObjectList sortieren

Beitrag von wp_xyz »

fliegermichl hat geschrieben:
Mi 13. Mär 2024, 10:57
ArchChem hat geschrieben:
Di 12. Mär 2024, 20:56
...

Code: Alles auswählen

function CompareStressList(item1: TStressEntry; item2: TStressEntry): integer;
begin
  // Definiere, was passiert, falls einer der Vergleichsparameter nil ist.
  if (item1.id = nil) and (item2.id = nil) then exit(0);
  if item1.id = nil then exit(-1);
  if item2.id = nil then exit(1);
...  
Ich denke, der Vergleich ist falsch herum. Wenn Item1.id = nil, dann ist Item2 größer und umgekehrt.
Naja, je nachdem, wo die leeren Records stehen sollen, am Anfang oder am Ende der Liste...

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1436
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: TObjectList sortieren

Beitrag von fliegermichl »

Das ist freilich wahr.

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: TObjectList sortieren

Beitrag von photor »

ArchChem hat geschrieben:
Di 12. Mär 2024, 20:56
Hallo Photor,

wenn die Knotennummer die ID ist, dann könnte deine Vergleichsfunktion in etwa so aussehen:

Code: Alles auswählen

function CompareStressList(item1: TStressEntry; item2: TStressEntry): integer;
begin
  // Definiere, was passiert, falls einer der Vergleichsparameter nil ist.
  if (item1.id = nil) and (item2.id = nil) then exit(0);
  if item1.id = nil then exit(-1);
  if item2.id = nil then exit(1);
  
  // Der normale Vergleich
  if item1.id < item2.id then Result := -1
  else if item1.id = item2.id then Result := 0
  else if item1.id > item2.id then Result := 1;
end;
[...]
Sowas in der Art hatte ich selbst versucht; aber - es compiliert nicht;

Code: Alles auswählen

...
Error: Operator is not overloaded: "Longint" = "Pointer"
...
Error: Incompatible type of arg no. 1 Got "<address of function(Pointer;Pointer):Longint;Register>", expected "iComparer<xxxx.TStressEntry>'
...   
(abgetippt: ich hoffe, ich habe alle Zeichen richtig übertragen)
So wie ich das interpretiere, werden Pointer für Item1 und Item2 erwartet. Übergeben werden aber TStressEntry-Klassen..

Wenn ich Pointer übergebe, komme ich(!) aber nicht an die Struktur des Eintrags, also die ID -heran. Das ist mein Problem.

Ciao,
Photor

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: TObjectList sortieren

Beitrag von photor »

af0815 hat geschrieben:
Mi 13. Mär 2024, 06:23
Ich würde eine nach ID sortierte Liste machen da ev. eine Hashliste und einfach Zeile für Zeile aus der jeweiligen Datei lesen bzw. das zuerst in eine Stringliste einlesen und das ganze aus dieser Liste exportieren. Damit kann man auch leichter damit umgehen, falls eine ID in einer Liste fehlt, bzw die Listen nicht ganz gleich sind. Ausserdem ist es dann egal, ob du eine oder fünfzig Listen einliest.
Das wäre ungefähr die Idee, die ich im PS meines 1. Post skiziert hatte, richtig?

Eigentlich, will ich ja die 3 Listen so zusammen führen, dass die Werte, die jedem Knoten zugeordnet sind, zu einem Tensor/Vektor zusammen gefasst werden. Dazu hatte ich direkt ein TDictionary mit KnotenID als Key und dem Vektor als Value definiert, und dann mit der ersten Liste das Dictionary gefüllt, und wollte dann den 2. und 3. Wert jeweils im Vektor des Knotens speichern.

Das hat leider nicht funktioniert, weil wohl beim Einleser der 2./3. Liste der Eintrag nicht gefunden wurde (und dann neu angehängt wurde).

Ciao,
Photor

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1436
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: TObjectList sortieren

Beitrag von fliegermichl »

Die ListsortCompare Funktion ist mit Pointerparametern definiert, weil ja alles mögliche in der Liste stehen kann.
Übergeben werden aber immer die tatsächlich in der Liste gespeicherten Objekte - und das sind in Deinem Fall die TStressEntry.

Da Id als Integer definiert ist, kannst du das natürlich auch als Integer vergleichen.
In dem Fall kann die Vergleichsroutine ganz einfach so aussehen.

Code: Alles auswählen

function CompareStressList(item1: TStressEntry; item2: TStressEntry): integer;
begin
 Result := Item1.Id - Item2.Id;
end;

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: TObjectList sortieren

Beitrag von photor »

Moin,

damit steigt der Compiler jetzt an der Stelle aus:

Code: Alles auswählen

  // sort the ObjectLists for NodeID
  StressList_Min.Sort(@CompareStressList);
  StressList_Int.Sort(@CompareStressList);
  StressList_Max.Sort(@CompareStressList);
Mit der Fehlermeldung (jetzt gefunden, wie man die Fehlermeldungen direkt aus der Ausgabe kopiert):

Code: Alles auswählen

sm_help.pas(297,41) Error: Incompatible type for arg no. 1: Got "<address of function(TStressEntry;TStressEntry):LongInt;Register>", expected "IComparer<sm_Help.TStressEntry>"
objpas.pp(296,15) Hint: Found declaration: Sort(const IComparer$1$crcE218F512);
Ich glaube, an der Stelle ist Zeit für ein Demo-Projekt; ich hoffe, ich komme da heute noch zu.

Ciao,
Photor

Benutzeravatar
photor
Beiträge: 445
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux: L 2.2.6 FPC 3.2.2 (Gtk2)
CPU-Target: 64Bit

Re: TObjectList sortieren

Beitrag von photor »

Hallo,

hier ein Beispiel; der Einfachheit halber als Konsolen-Programm:

Code: Alles auswählen

program testproject;

{$mode ObjFPC}{$H+}

uses
  Classes, SysUtils, Generics.Collections, Math;

type

  TStressEntry = class
    ID: integer;
    stress: double;
  end;

  TStressList = specialize TObjectList<TStressEntry>;

var
  i: integer;
  NodeID: integer;
  Stress: double;
  StrLst, Line: TstringList;
  str: string;
  StressEntry: TStressEntry;
  StressList: TStressList;


function CompareStressList(item1: TStressEntry; item2: TStressEntry): integer;
begin
 Result := Item1.Id - Item2.Id;
end;


begin
  StressList := TStressList.Create;

  StrLst := TStringList.Create;

  //Simulates LoadFromFile
  StrLst.Add('513384 -4.821229171753e+01');
  StrLst.Add('806394 -4.821158599854e+01');
  StrLst.Add('513934 -4.778438568115e+01');
  StrLst.Add('806944 -4.778303909302e+01');
  StrLst.Add('809518 -4.754859924316e+01');
  StrLst.Add('516512 -4.754709625244e+01');
  StrLst.Add('517078 -4.745193862915e+01');
  StrLst.Add('810084 -4.744944381714e+01');
  StrLst.Add('513247 -4.690411376953e+01');
  StrLst.Add('806257 -4.690361785889e+01');
  StrLst.Add('513851 -4.689748382568e+01');
  StrLst.Add('806861 -4.689499282837e+01');
  StrLst.Add('513713 -4.639287948608e+01');
  StrLst.Add('806723 -4.639037704468e+01');
  StrLst.Add('514801 -4.603351974487e+01');
  StrLst.Add('807807 -4.602876281738e+01');
  StrLst.Add('515922 -4.540652465820e+01');
  StrLst.Add('808928 -4.540190505981e+01');

  for i:=0 to StrLst.Count-1 do
  begin
    str := StrLst[i];

    Line := TStringList.Create;
    Line.DelimitedText := str;

    NodeID := StrToInt(Line[0]);
    Stress := StrToFloat(Line[1]);

    writeln(Format('  %2d:   %6d -- %12.6f',[i,NodeID,Stress]));

    StressEntry := TStressEntry.Create;

    StressEntry.ID := NodeID;
    StressEntry.stress := Stress;

    StressList.Add(StressEntry);

    Line.Free;
  end;

  writeln(Format(' length of StressList: %d',[StressList.Count]));

  for i:=0 to StressList.Count-1 do
  begin
    writeln(Format(' StressList[%2d]:   %6d -- %12.6f',
      [i,StressList[i].ID,StressList[i].Stress]));
  end;

  // Now sort
  StressList.Sort(@CompareStressList);      // <--- hier stoppt der Compiler

  for i:=0 to StressList.Count-1 do
  begin
    writeln(Format('NOW SORTED - StressList[%2d]:   %6d -- %12.6f',
      [i,StressList[i].ID,StressList[i].Stress]));
  end;

end. 
Ich denke, das entspricht dem, was hier zuletzt diskutiert wurde. Und dem, was ich in meinem Projekt bisher verbaut habe (Einlesen der TXT-Datei mittels StringList.LoadFromFile() ist durch eine fest definierte StringList ersetzt).

Der Compiler steigt mit der oben schon gezeigten Stelle aus - der Code bis dahin läuft.

Ciao,
Photor

BTW: bin ich blind, oder gibt es kein Fenster/Tab in der IDE, die die Konsolenausgabe zeigt, wenn ich das Programm in der IDE starte?

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1436
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: TObjectList sortieren

Beitrag von fliegermichl »

Oh, da hat man TListSortCompare entfernt und das mit einem Interface IComparer umgebaut.
Deine zu sortierende Klasse kann jetzt eine Function OnComparison definieren, die dann aufgerufen wird.
Ich hab das mal umgebastelt.

Ich habe deutsche Formatsettings, deshalb habe ich den Trennpunkt bei den Floatkonstanten durch Kommata ersetzt.

Code: Alles auswählen

program testproject;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils, Generics.Collections, Math;

type

  { TStressEntry }

  TStressEntry = class
    ID: integer;
    stress: double;
    function OnComparison(Left, Right : TStressEntry) : integer;
  end;

  TStressList = specialize TObjectList<TStressEntry>;

var
  i: integer;
  NodeID: integer;
  Stress: double;
  StrLst, Line: TstringList;
  str: string;
  StressEntry: TStressEntry;
  StressList: TStressList;
  L1, L2 : string;

{ TStressEntry }

function TStressEntry.OnComparison(Left, Right: TStressEntry): integer;
begin
  Result := Left.Id - Right.Id;
end;

begin
  StressList := TStressList.Create;

  StrLst := TStringList.Create;

  //Simulates LoadFromFile
  StrLst.Add('513384 -4,821229171753e+01');
  StrLst.Add('806394 -4,821158599854e+01');
  StrLst.Add('513934 -4,778438568115e+01');
  StrLst.Add('806944 -4,778303909302e+01');
  StrLst.Add('809518 -4,754859924316e+01');
  StrLst.Add('516512 -4,754709625244e+01');
  StrLst.Add('517078 -4,745193862915e+01');
  StrLst.Add('810084 -4,744944381714e+01');
  StrLst.Add('513247 -4,690411376953e+01');
  StrLst.Add('806257 -4,690361785889e+01');
  StrLst.Add('513851 -4,689748382568e+01');
  StrLst.Add('806861 -4,689499282837e+01');
  StrLst.Add('513713 -4,639287948608e+01');
  StrLst.Add('806723 -4,639037704468e+01');
  StrLst.Add('514801 -4,603351974487e+01');
  StrLst.Add('807807 -4,602876281738e+01');
  StrLst.Add('515922 -4,540652465820e+01');
  StrLst.Add('808928 -4,540190505981e+01');

  for i:=0 to StrLst.Count-1 do
  begin
    str := StrLst[i];

    Line := TStringList.Create;
    Line.DelimitedText := str;

    L1 := Line[0];
    L2 := Line[1];
    NodeID := StrToInt(L1);
    Stress := StrToFloat(L2);

    writeln(Format('  %2d:   %6d -- %12.6f',[i,NodeID,Stress]));

    StressEntry := TStressEntry.Create;

    StressEntry.ID := NodeID;
    StressEntry.stress := Stress;

    StressList.Add(StressEntry);

    Line.Free;
  end;

  writeln(Format(' length of StressList: %d',[StressList.Count]));

  for i:=0 to StressList.Count-1 do
  begin
    writeln(Format(' StressList[%2d]:   %6d -- %12.6f',
      [i,StressList[i].ID,StressList[i].Stress]));
  end;

  // Now sort
  StressList.Sort;
  //StressList.Sort(@CompareStressList);      // <--- hier stoppt der Compiler

  for i:=0 to StressList.Count-1 do
  begin
    writeln(Format('NOW SORTED - StressList[%2d]:   %6d -- %12.6f',
      [i,StressList[i].ID,StressList[i].Stress]));
  end;
  ReadLn;
end.

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 338
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon (Windows wenn notwendig), Lazarus 3.0 FPC 3.3.1

Re: TObjectList sortieren

Beitrag von Niesi »

Habe ich das richtig verstanden: Du hast mehrere Textfiles, in denen pro Zeile je eine Knotennummer und - durch Leerzeichen getrennt - ein Spannungswert stehen?

Die führst Du zu einem Textfile zusammen und brauchst die dann nach Knotennummer sortiert?
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1436
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: TObjectList sortieren

Beitrag von fliegermichl »

Mein voriger Beitrag ist falsch. Es compiliert zwar aber sortiert nicht. Der Comparer muß beim TStressList.Create angegeben werden.

Antworten