einem string einen Wert zuweisen (bzw. schnell suchen)
einem string einen Wert zuweisen (bzw. schnell suchen)
Hallo zusammen,
Folgendes: Ich habe eine größere Objectlist, in der ich Daten in Form einzelner Objekte ablege (im Grunde ist das eine Vokabelliste). Zusätzlich habe ich einen abermals recht großen Text, den ich in strings zerlege. Ich weiß, dass es zu jedem string genau ein Objekt gibt, das diesen enthält. Nun möchte ich dieses Objekt möglichst schnell finden, bzw. auf dessen übrige Daten zugreifen.
ein traditionelles Durchscannen nach Schema: for i:=0 to Count(ObjectList) usw. ist mir definitiv zu langsam, da ich die Funktion ständig brauche, so dass sich das ganze Gesuche fix aufsummiert und zu untragbaren Laufzeiten führt
Lieb wäre es mir, wenn ich einem string einmalig (idealerweise beim Laden der ObjectListe) einen Wert (nämlich den Index für die oben erwähnte Objectlist) zuweisen könnte, so dass ich später über den string direkt auf diesen Wert zugreifen kann, ohne jedesmal eine Suchroutine laufen lassen zu müssen.
Oder anders ausgedrückt: Ich suche quasi das Gegenstück zu einem 'Zeugs: array[1..999999] of string', Dort kann ich ja ohne Suche auf den string zu Wert 12345 zugreifen, indem ich bspw. 'mystring:=Zeugs[12345]' verwende... Ich will nun (möglichst ohne Suche, alternativ mit sehr schneller Suche) sowas wie i:=Zeugs[mystring] haben.
welche Optionen habe ich?
Folgendes: Ich habe eine größere Objectlist, in der ich Daten in Form einzelner Objekte ablege (im Grunde ist das eine Vokabelliste). Zusätzlich habe ich einen abermals recht großen Text, den ich in strings zerlege. Ich weiß, dass es zu jedem string genau ein Objekt gibt, das diesen enthält. Nun möchte ich dieses Objekt möglichst schnell finden, bzw. auf dessen übrige Daten zugreifen.
ein traditionelles Durchscannen nach Schema: for i:=0 to Count(ObjectList) usw. ist mir definitiv zu langsam, da ich die Funktion ständig brauche, so dass sich das ganze Gesuche fix aufsummiert und zu untragbaren Laufzeiten führt
Lieb wäre es mir, wenn ich einem string einmalig (idealerweise beim Laden der ObjectListe) einen Wert (nämlich den Index für die oben erwähnte Objectlist) zuweisen könnte, so dass ich später über den string direkt auf diesen Wert zugreifen kann, ohne jedesmal eine Suchroutine laufen lassen zu müssen.
Oder anders ausgedrückt: Ich suche quasi das Gegenstück zu einem 'Zeugs: array[1..999999] of string', Dort kann ich ja ohne Suche auf den string zu Wert 12345 zugreifen, indem ich bspw. 'mystring:=Zeugs[12345]' verwende... Ich will nun (möglichst ohne Suche, alternativ mit sehr schneller Suche) sowas wie i:=Zeugs[mystring] haben.
welche Optionen habe ich?
-
- Beiträge: 210
- Registriert: Do 24. Jan 2013, 21:22
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Hallo mulcheo ,
nimm doch tstrings.indexof.
nimm doch tstrings.indexof.
Code: Alles auswählen
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
Edit2: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
memo1.clear;
Memo1.lines.add('Berta');
Memo1.lines.add('Penelopee');
Memo1.lines.add('Klara');
Memo1.lines.add('Otto');
Memo1.lines.add('Kalle');
Memo1.lines.add('Fritz');
end;
procedure TForm1.Button2Click(Sender: TObject);
var s:string;
index:integer;
begin
s:=edit1.text;
index:=memo1.Lines.IndexOf(s);
edit2.text:=inttostr(index);
end;
end.
- Dateianhänge
-
String_index_Suche.zip
- (1016.48 KiB) 47-mal heruntergeladen
www.flz-vortex.de
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
soweit ich es sehe, macht indexof aber auch nichts weiter als eine sequentielle Suche durch die gesamte Stringlist und das ist ja gearde der zeitfressende part, den ich vermeiden will. Wenn die Liste sortiert ist, läuft die Suche nach meiner Recherche schneller (via binary cut, glaube ich) aber dann gibt mir der Index natürlich kein sinnvolles Ergebnis mehr. ist ein workaround, über den ich nachgedacht habe, aber dann habe ich wieder mehrere StrToInt() bzw. IntToStr() im code, was suboptimal ist (AddPair arbeitet mit strings);
Code: Alles auswählen
var
Liste: TStringList;
myWert: integer;
s,mystring: string;
Liste.AddPair(mystring,IntToStr(myWert));
// code
Liste.sort;
s:=Liste.ValueFromIndex(Liste.indexof(mystring));
myWert:=StrToInt(s);
Zuletzt geändert von mulcheo am Do 11. Aug 2022, 09:48, insgesamt 5-mal geändert.
-
- Beiträge: 950
- Registriert: Mi 3. Jun 2020, 07:18
- OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
- CPU-Target: Aarch64 bis Z80 ;)
- Wohnort: München
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Du könntest dir mal fgl.TFPGMapObject<,> oder Generics.Collections.TDictionary<,> anschauen (beide arbeiten mit Hashlisten des Key Typparameters):
Code: Alles auswählen
{$mode objfpc}{$H+}
type
TVokabelInfo = class
// was auch immer
end;
TVokabelListe = specialize TFPGMapObject<String, TVokabelInfo>;
var
vl: TVokabelListe;
begin
vl := TVokabelListe.Create;
try
vl.Add('Foobar', TVokabelInfo.Create);
vl.Add('Blubb', TVokabelInfo.Create);
Writeln(vl.IndexOf('Blubb'));
Writeln(vl.IndexOf('Whatever'));
v := vl['Foobar'];
// was auch immer
finally
vl.Free;
end;
end.
FPC Compiler Entwickler
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
@PascalDragon: Danke für das Beispiel, das kann ich bestimmt auch mal gebrauchen.
Ist wahrscheinlich eine optimale Lösung für das Problem.
braucht es noch.
Ist wahrscheinlich eine optimale Lösung für das Problem.
Code: Alles auswählen
uses fgl;
- fliegermichl
- Lazarusforum e. V.
- Beiträge: 1639
- Registriert: Do 9. Jun 2011, 09:42
- OS, Lazarus, FPC: Lazarus Fixes FPC Stable
- CPU-Target: 32/64Bit
- Wohnort: Echzell
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Zu erwähnen wäre auch noch, daß vl.sorted auf true gesetzt werden sollte, da ansonsten auch sequenziell gesucht wird.
- fliegermichl
- Lazarusforum e. V.
- Beiträge: 1639
- Registriert: Do 9. Jun 2011, 09:42
- OS, Lazarus, FPC: Lazarus Fixes FPC Stable
- CPU-Target: 32/64Bit
- Wohnort: Echzell
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Ich schätze, daß ich dieses specialize noch nicht so ganz durchschaut habe.
Kann ich nach der Zuweisung von v := vl['foobar'] auf den String 'foobar' zugreifen?
Kann ich nach der Zuweisung von v := vl['foobar'] auf den String 'foobar' zugreifen?
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Auf das Objekt: Ja.fliegermichl hat geschrieben: Do 11. Aug 2022, 10:48 Ich schätze, daß ich dieses specialize noch nicht so ganz durchschaut habe.
Kann ich nach der Zuweisung von v := vl['foobar'] auf den String 'foobar' zugreifen?
Sowas geht:
Code: Alles auswählen
Caption:=vl['Foobar'].fTest;
Code: Alles auswählen
type
TVokabelInfo = class
public
fTest:String;
end;
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
@PascalDragon: Bist du sicher, dass TFPGMapObject Hashlisten hält?
In Unit fgl finde ich keinen Hinweis darauf.
in Generics.Collections ist viel von "Hash" die Rede.
In Unit fgl finde ich keinen Hinweis darauf.
in Generics.Collections ist viel von "Hash" die Rede.
- fliegermichl
- Lazarusforum e. V.
- Beiträge: 1639
- Registriert: Do 9. Jun 2011, 09:42
- OS, Lazarus, FPC: Lazarus Fixes FPC Stable
- CPU-Target: 32/64Bit
- Wohnort: Echzell
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Das ist klar, dann muß der String aber doppelt gespeichert werden. 'foobar' muß dann ja an v.fTest zugewiesen werden.
Ich dachte eher an so etwas wie writeln(vl[v].Key);
Ich dachte eher an so etwas wie writeln(vl[v].Key);
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Ich weiss es auch nicht und es würde mich auch interessieren.fliegermichl hat geschrieben: Do 11. Aug 2022, 11:17 Das ist klar, dann muß der String aber doppelt gespeichert werden. 'foobar' muß dann ja an v.fTest zugewiesen werden.
Ich dachte eher an so etwas wie writeln(vl[v].Key);
Es hängt aber mMn mit der Hash Frage zusammen.
Wenn der String nur als Identifikator herhalten muss, kann man ihn auch in einen Hash umwandeln und muss ihn als String gar nicht speichern.
Wie gesagt, ich verstehe davon nicht viel, aber das wäre für mich logisch und speicher-effizient.
An sich macht es ja wenig Sinn, den String, den ich schon habe noch mal von vl zurückzubekommen.
-
- Beiträge: 950
- Registriert: Mi 3. Jun 2020, 07:18
- OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
- CPU-Target: Aarch64 bis Z80 ;)
- Wohnort: München
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Stimmt...

Stimmt auchfliegermichl hat geschrieben: Do 11. Aug 2022, 10:44 Zu erwähnen wäre auch noch, daß vl.sorted auf true gesetzt werden sollte, da ansonsten auch sequenziell gesucht wird.

Grad nochmal nachgeschaut, und ja, es TFPGMapObject<,> nutzt keine Hashes, aber durch die Sortierung kann es eine binäre Suche machen, was die Sache auch beschleunigt im Vergleich zu einer linearen Suche durch die ganze Liste.theo hat geschrieben: Do 11. Aug 2022, 11:15 @PascalDragon: Bist du sicher, dass TFPGMapObject Hashlisten hält?
In Unit fgl finde ich keinen Hinweis darauf.
in Generics.Collections ist viel von "Hash" die Rede.
Wenn du den Index deines Wertes weißt, dann kannst du ihn mittels vl.Keys[idx] abfragen. Ein umgekehrtes Mapping gibt es nicht, da der Data-Wert mehrfach verwendet werden könnte. Aber wenn du deinen Key-Wert eh schon hast, brauchst du ihn nicht nochmal abfragen. Du musst ihn halt dann an eventuelle Funktionen mit weiterreichen oder ihn im Objekt selbst mit abspeichern (solange du den gleichen Stringwert sowohl für den Key als auch das Feld in den Daten verwendest, ist sein Inhalt auch nicht doppelt im Speicher, da Strings ja Copy-On-Write sind; du hast also nur einen zusätzlichen Pointer als Overhead).fliegermichl hat geschrieben: Do 11. Aug 2022, 11:17 Das ist klar, dann muß der String aber doppelt gespeichert werden. 'foobar' muß dann ja an v.fTest zugewiesen werden.
Ich dachte eher an so etwas wie writeln(vl[v].Key);
FPC Compiler Entwickler
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Weil mich das mit der Hashlist jetzt doch noch interessiert hat und ich nicht direkt ein Beispiel für TDictionary gefunden habe, hier ein Test.
Ist das richtig angewandt?
Gibt es noch einen Trick, wie man die Objekte freigibt? Mit "doOwnsValues" bin ich nicht schlau geworden.
Ist das richtig angewandt?
Code: Alles auswählen
uses
...Generics.Collections;
...
type
{ TVokabelInfo }
TVokabelInfo = class
private
fTest: string;
public
constructor Create(ATest: string);
property Test: string read fTest write fTest;
end;
TVokabelDict = class(specialize TDictionary<string, TVokabelInfo>);
.....
constructor TVokabelInfo.Create(ATest: string);
begin
inherited Create;
fTest := ATest;
end;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
VokDict: TVokabelDict;
begin
VokDict := TVokabelDict.Create;
VokDict.Add('eins', TVokabelInfo.Create('Test_1'));
VokDict.Add('zwei', TVokabelInfo.Create('Test_2'));
ShowMessage(VokDict.Items['zwei'].Test);
VokDict.Free;
end;
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Habe mittlerweile einen Weg gefunden, die Objekte freizugeben.
Scheint mir aber etwas umständlich und ist für mich ein Blindflug.
Immerhin ist heaptrace so zufrieden.
Gibt es andere Möglichkeiten? Gibt es Beispiele für TDictionary?
Scheint mir aber etwas umständlich und ist für mich ein Blindflug.
Immerhin ist heaptrace so zufrieden.
Gibt es andere Möglichkeiten? Gibt es Beispiele für TDictionary?
Code: Alles auswählen
uses ..., Generics.Collections;
...
type
{ TVokabelInfo }
TVokabelInfo = class
private
fTest: string;
public
constructor Create(ATest: string);
property Test: string read fTest write fTest;
end;
TMyPair = specialize TPair<string, TVokabelInfo>;
TVokabelDict = class(specialize TDictionary<string, TVokabelInfo>);
...
{ TVokabelInfo }
constructor TVokabelInfo.Create(ATest: string);
begin
inherited Create;
fTest := ATest;
end;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
VokDict: TVokabelDict;
Vi: TMyPair;
begin
VokDict := TVokabelDict.Create;
VokDict.Add('eins', TVokabelInfo.Create('Test_1'));
VokDict.Add('zwei', TVokabelInfo.Create('Test_2'));
ShowMessage(VokDict.Items['zwei'].Test);
for Vi in VokDict do Vi.Value.Free; //Freigabe
VokDict.Free;
end;
-
- Beiträge: 950
- Registriert: Mi 3. Jun 2020, 07:18
- OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
- CPU-Target: Aarch64 bis Z80 ;)
- Wohnort: München
Re: einem string einen Wert zuweisen (bzw. schnell suchen)
Du musst ein TObjectDictionary<,>, statt nur TDictionary<,> verwenden und dann im Konstruktor [doOwnsValues] als Parameter übergeben.theo hat geschrieben: Do 11. Aug 2022, 19:16 Gibt es noch einen Trick, wie man die Objekte freigibt? Mit "doOwnsValues" bin ich nicht schlau geworden.
FPC Compiler Entwickler