Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Duplicate Values in TDictionary (https://www.delphipraxis.net/184801-duplicate-values-tdictionary.html)

bernhard_LA 22. Apr 2015 08:32

Duplicate Values in TDictionary
 
hier ein kurzer Testcode

Delphi-Quellcode:
unit Unit1xxx;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, generics.collections,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TElement = class
    name: string;
    id: integer;
    value: string;
    constructor create(aname: string; aid: integer);
  end;

  TFrequencyList = class
    data: array of integer;
    constructor create(n: integer);
  end;

type
  TForm1 = class(TForm)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btn1Click(Sender: TObject);
var
  dict: TDictionary<TElement, TFrequencyList>;
  Myelement: TElement;
  FCount: TFrequencyList;
begin

  dict := TDictionary<TElement, TFrequencyList>.create;

  /// code version #1
  Myelement := TElement.create('maier', 0);
  FCount := TFrequencyList.create(6);
  FCount.data[1] := 99;
  dict.Add(Myelement, FCount);

  /// code version #2
  dict.Add(Myelement.create('mueller', 1), FCount.create(5));

  // Find some key
  Myelement := TElement.create('maier', 99);
  if dict.TryGetValue(Myelement, FCount) then
  begin
    ShowMessage('Found it!: ' + IntToStr(FCount.data[1]));
  end;

  Myelement := TElement.create('maier', 0);
  if dict.TryGetValue(Myelement, FCount) then
  begin
    ShowMessage('Found it!: ' + IntToStr(FCount.data[1]));
  end;

end;

{ TFrequencyList }

constructor TFrequencyList.create(n: integer);
begin
  setlength(data, n);
end;

{ TElement }

constructor TElement.create(aname: string; aid: integer);
begin
  self.name := aname;
  self.id := aid;
  self.value := 'dummy data';
end;

end.

Delphi behauptet beim Einfügen des zweiten Datensatzes "Duplicate nicht zulässig" , ich sehe den Fehler nicht ....

TiGü 22. Apr 2015 08:46

AW: Duplicate Values in TDictionary
 
Von
Delphi-Quellcode:
  /// code version #2
  dict.Add(Myelement.create('mueller', 1), FCount.create(5));
Nach
Delphi-Quellcode:
  /// code version #2
  dict.Add(TElement.create('mueller', 1), FCount.create(5));
ändern!

Neutral General 22. Apr 2015 08:48

AW: Duplicate Values in TDictionary
 
Warum die Meldung mit den Duplikaten kommt kann ich dir nicht sagen, aber ein Objekt als Key zu nehmen ist nicht so praktisch. Vor allem kannst du auf die Weise nicht überprüfen ob ein Objekt schon in der Liste enthalten ist.

Denn die Objekte sind nur Pointer (Zahlen) und dieser Pointer ist grob gesagt für jedes Objekt unterschiedlich, selbst wenn die Daten des Objekts identisch sind.
Damit das funktioniert musst du nach EXAKT dem Objekt suchen was du beim Add angegeben hast, nicht einer Kopie mit denselben Werten.

@TiGü: Stimmt. Aber statt FCount.Create muss da auch TFrequencyList.Create stehen.

quaero 22. Apr 2015 08:54

AW: Duplicate Values in TDictionary
 
Hallo,

mit
Code:
Myelement.create('mueller', 1)
änderst du nur die Instanz Myelement. Das hat Auswirkungen auf alle Stellen, an denen du mit genau dieser Instanz arbeitest, also auch auf den Schlüssel im Dictionary. Deswegen auch die Fehlermeldung, dass der Schlüssel schon exisitert. Um ein weiteres Wertepaar ins Dictionary einzutragen, solltest du zwei neue Instanzen erstellen mit
Code:
dict.Add(Telement.create('mueller', 1), TFrequencyList.create(5));
Danach erstellst du zwei neue Instanzen mit:
Code:
  Myelement := TElement.create('maier', 99);
Code:
    Myelement := TElement.create('maier', 0);
Diese Instanzen werden natürlich nicht im Dictionary gefunden, da si nicht eingefügt wurden. Selbst wenn du die Zeile rausnimmst, die den Fehler verursacht, werden die neuen Instanzen nicht im Dictionary gefunden. Auch wenn die gleichen Werte in der Klasse gespeichert sind, so handelt es sich dennoch um zwei unterschiedliche Instanzen. Um Instanzen mit gleichen Werten zu vergleichen musst du einen eigenen Comparer schreiben und beim Erstellen des Dictionary mitgeben.

Sir Rufo 22. Apr 2015 10:40

AW: Duplicate Values in TDictionary
 
Zitat:

Zitat von Neutral General (Beitrag 1298747)
Warum die Meldung mit den Duplikaten kommt kann ich dir nicht sagen, aber ein Objekt als Key zu nehmen ist nicht so praktisch. Vor allem kannst du auf die Weise nicht überprüfen ob ein Objekt schon in der Liste enthalten ist.

Denn die Objekte sind nur Pointer (Zahlen) und dieser Pointer ist grob gesagt für jedes Objekt unterschiedlich, selbst wenn die Daten des Objekts identisch sind.
Damit das funktioniert musst du nach EXAKT dem Objekt suchen was du beim Add angegeben hast, nicht einer Kopie mit denselben Werten.

Genau dafür gibt es die Comparer ...

bernhard_LA 22. Apr 2015 11:01

AW: Duplicate Values in TDictionary
 
Problem #1 mit den Duplikaten ist gelöst; Verbleibt die Frage warum er Die Elemente dann nicht in meinem Wörterbuch findet ?


Delphi-Quellcode:
  dict: TDictionary<TElement, TFrequencyList>;
  Myelement: TElement;
  FCount: TFrequencyList;
begin

  dict := TDictionary<TElement, TFrequencyList>.create;

  /// code version #1
  Myelement := TElement.create('maier', 0);
  FCount := TFrequencyList.create(6);
  FCount.data[1] := 99;
  dict.Add(Myelement, FCount);

  /// code version #2
  dict.Add(Telement.create('mueller', 1), TFrequencyList.create(5));

  // Find some key -> hier sollte man nix finden ....
  Myelement := TElement.create('maier', 99);
  if dict.TryGetValue(Myelement, FCount) then
  begin
    ShowMessage('Found it!: ' + IntToStr(FCount.data[1]));
  end;

  ///  gibt den hier gibt es doch :-(   ????????????????????????
  Myelement := TElement.create('maier', 0);
  if dict.TryGetValue(Myelement, FCount) then
  begin
    ShowMessage('Found it!: ' + IntToStr(FCount.data[1]));
  end;

end;

Neutral General 22. Apr 2015 11:02

AW: Duplicate Values in TDictionary
 
Zitat:

Zitat von bernhard_LA (Beitrag 1298779)
Problem #1 mit den Duplikaten ist gelöst; Verbleibt die Frage warum er Die Elemente dann nicht in meinem Wörterbuch findet ?

Dann lies die letzten paar Antworten nochmal. Da steht nämlich sowohl der Grund als auch mindestens eine Lösung dafür drin.

Sir Rufo 22. Apr 2015 11:29

AW: Duplicate Values in TDictionary
 
Zitat:

Zitat von bernhard_LA (Beitrag 1298779)
Problem #1 mit den Duplikaten ist gelöst; Verbleibt die Frage warum er Die Elemente dann nicht in meinem Wörterbuch findet ?

Ganz einfach, weil der DefaultComparer für
Delphi-Quellcode:
TObject
und alle Ableitungen ganz simpel über die Referenz geht.

Und unterschiedliche Instanzen haben auch unterschiedliche Referenzen.

Also musst du dem Dictionary einen Comparer mitgeben, der auf deine gewünschte Gleichheit prüft.

bernhard_LA 22. Apr 2015 14:50

AW: Duplicate Values in TDictionary
 
ich habe einen TCustomComparer erstellt, nur bekomme ich einen ABSTRAKTEN Fehler beim Einfügen des zweiten Datensatzes in mein TDictionary



Delphi-Quellcode:
type
  TElement = class
    name: string;
    id: integer;
    value: string;
    constructor create(aname: string; aid: integer);
  end;

  TFrequencyList = class
    data: array of integer;
    constructor create(n: integer);
  end;

  TMyComparer = class(TCustomComparer<TElement>)
    function Compare(const Left, Right: TElement): integer; override;
  end;

type
  TForm1 = class(TForm)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btn1Click(Sender: TObject);
var
  dict: TDictionary<TElement, TFrequencyList>;
  Myelement: TElement;
  FCount: TFrequencyList;
begin

  dict := TDictionary<TElement, TFrequencyList>.create( TMyComparer.create) ;

  /// code version #1
  Myelement := TElement.create('maier', 0);
  FCount := TFrequencyList.create(6);
  FCount.data[1] := 99;
  dict.Add(Myelement, FCount);

  /// code version #2
  dict.Add(TElement.create('mueller', 1), TFrequencyList.create(5));

  // Find some key -> sollten nix finden
  Myelement := TElement.create('maier', 99);
  if dict.TryGetValue(Myelement, FCount) then
  begin
    ShowMessage('Found it!: ' + IntToStr(FCount.data[1]));
  end;

  /// gibt es doch :-(
  Myelement := TElement.create('maier', 0);
  if dict.TryGetValue(Myelement, FCount) then
  begin
    ShowMessage('Found it!: ' + IntToStr(FCount.data[1]));
  end;

end;

{ TFrequencyList }

constructor TFrequencyList.create(n: integer);
begin
  setlength(data, n);
end;

{ TElement }

constructor TElement.create(aname: string; aid: integer);
begin
  self.name := aname;
  self.id := aid;
  self.value := 'dummy data';
end;

{ TMyComparer }

function TMyComparer.Compare(const Left, Right: TElement): integer;
begin
     result := Comparetext(left.name, right.name);
end;

end.

Neutral General 22. Apr 2015 14:53

AW: Duplicate Values in TDictionary
 
Du musst für deinen Comparer die Methoden Compare,Equals und GetHashCode überschreiben. Nur Compare reicht nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:42 Uhr.

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