AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Feld sortieren und damit weiterarbeiten

Ein Thema von Ultimator · begonnen am 1. Apr 2015 · letzter Beitrag vom 2. Apr 2015
Antwort Antwort
Benutzerbild von Ultimator
Ultimator

Registriert seit: 17. Feb 2004
Ort: Coburg
1.860 Beiträge
 
FreePascal / Lazarus
 
#1

Feld sortieren und damit weiterarbeiten

  Alt 1. Apr 2015, 09:17
Hallo zusammen,

nach langer Zeit bin ich mal wieder hier unterwegs und hab auch wieder eine Frage Der Titel ist evtl. etwas blöd, aber ich weiß nicht, wie ich das anders kurz in Worte fassen soll

Ich hab eine Klasse, die ein Datenarray enthält. Das möchte ich sortieren; funktioniert auch problemlos. Jetzt kann ich das z.B. so implementieren:
Delphi-Quellcode:
type
  TDatenArray = array of TDaten;
  
  MeineKlasse = class(TObject)
  private
    FDaten: TDatenArray;
    procedure Daten_Sortieren; //Sortieralgorithmus ist hier ja unerheblich
  public
    property Daten: TDatenArray read FDaten;
  end;
Liegen die Daten in FDaten vor, kann ich Daten_Sortieren aufrufen. FDaten ist danach sortiert. Funktioniert, aber irgendwie ist mir dabei ein bisschen unwohl. Ich muss mir immer merken, ob und ab wo ich davon ausgehen kann, dass FDaten sortiert ist.

Wie kann ich das besser machen?
  • Über eine Eigenschaft Ist_Sortiert: boolean read FIst_Sortiert ?
  • Über eine gesonderte Eigenschaft Daten_Sortiert: TDatenarray read FDaten_Sortiert , so dass Daten_Sortieren mein Feld FDaten nicht überschreibt? Dann muss ich aber auch noch übeprüfen, ob FDaten_Sortiert schon gefüllt ist (evtl. auch über eine Eigenschaft Ist_Sortiert )
  • Statt procedure Daten_Sortieren eine function Daten_Sortieren: TDatenArray , so dass ich beim Aufruf der Methode den Rückgabewert in eine Variable meiner Wahl speichern kann/muss, statt die sortierten Daten im Objekt selbst vorzuhalten?

Irgendwie ist das eine grundsätzliche Frage für mich, die da mit rein spielt: Ein Objekt stellt die Prozeduren A und B bereit. A bearbeitet irgendwelche Daten (Felder) meines Objekts. B bearbeitet auch Felder und greift dabei auf die von A veränderten Felder zu. Wie regelt man das "schön"?

Viele Grüße
Ultimator
Julian J. Pracht
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Feld sortieren und damit weiterarbeiten

  Alt 1. Apr 2015, 10:33
Sieht so aus ob du eine property Sorted (also AutoSort) implementieren möchtest? Das hat allerdings Einfluß auf das Design der Klasse. Muß nach jedem OnChange der Daten ausgeführt werden. (Siehe z.B. TStringList). Z.B. deine property Daten geht dann so nicht mehr.
  Mit Zitat antworten Zitat
Benutzerbild von Ultimator
Ultimator

Registriert seit: 17. Feb 2004
Ort: Coburg
1.860 Beiträge
 
FreePascal / Lazarus
 
#3

AW: Feld sortieren und damit weiterarbeiten

  Alt 1. Apr 2015, 15:27
Danke Bjoerk!

Das bedeutet, ich müsste in der Klasse auch ein OnChange-Event meiner Daten zur Verfügung stellen und immer aufrufen, wenn ich etwas ändere?
Julian J. Pracht
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#4

AW: Feld sortieren und damit weiterarbeiten

  Alt 1. Apr 2015, 16:10
Das sollte die Klasse selbst übernehmen, wäre schicker? Ich schau mal nach wie TStringlist das macht und melde mich nochmal (Interessiert mich selber)..
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#5

AW: Feld sortieren und damit weiterarbeiten

  Alt 1. Apr 2015, 16:59
Bei einer StringList oder jeder Liste mit einem einfachen Daten-Typ ist das kein Problem, denn eine Änderung der Daten erfolgt durch den Setter der Listen-Klasse.
Delphi-Quellcode:
TStringList = class...
property Strings[Index:Integer] read GetString write SetString;
end;
Wird also der Setter aufgerufen, dann wird die Liste einfach neu sortiert.

Arbeite ich aber mit Klassen als Items, dann müsste ich zum automatischen Sortieren von diesen Instanzen eine Rückmeldung haben, ob sich in denen etwas geändert hat, denn Inhaltsänderungen an der Klasse laufen über diese Klasse selber und nicht über die Listen-Klasse.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: Feld sortieren und damit weiterarbeiten

  Alt 1. Apr 2015, 18:25
Und natürlich wenn sich Count ändert. Am besten, man spielt es an einem konkreten Beispiel durch.

Ungetestet:

Delphi-Quellcode:
unit uSortedDaten;

interface

uses
  Windows, SysUtils, Dialogs, Classes, Math;

type
  TDatenItem = record
    Id: integer;
    Name: string;
  end;

  TDatenSortFlag = (dsfId, dsfName);

  TDaten = class
  private
    FItems: array of TDatenItem;
    FSorted: boolean;
    FSortFlag: TDatenSortFlag;
    FOnChange, FOnChanging: TNotifyEvent;
    function Get(Index: integer): TDatenItem;
    function GetCount: integer;
    procedure Put(Index: integer; const Value: TDatenItem);
    procedure SetCount(Value: integer);
    procedure SetSorted(const Value: boolean);
    procedure SortExchange(Index1, Index2: integer);
    function SortCompare(const A, B: TDatenItem): integer;
    procedure QuickSort(L, R: integer);
    procedure Changed;
    procedure Changing;
  public
    procedure Clear;
    procedure Add(const Value: TDatenItem);
    procedure Delete(Index: integer);
    procedure Insert(Index: integer; const Value: TDatenItem);
    procedure Exchange(Index1, Index2: integer);
    procedure Sort;
    function IndexOfID(Value: integer): integer;
    procedure Assign(Value: TDaten);
    procedure LoadFromFile(const FileName: string);
    procedure SaveToFile(const FileName: string);
    destructor Destroy; override;
    property Count: integer read GetCount;
    property Sorted: boolean read FSorted write SetSorted;
    property SortFlag: TDatenSortFlag read FSortFlag write FSortFlag;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
    property Items[Index: integer]: TDatenItem read Get write Put; default;
  end;

implementation

type
  TFileStreamEx = class(TFileStream)
  private
    function ReadAnsiString: AnsiString;
  public
    function ReadInteger: integer;
    function ReadString: string;
    procedure WriteInteger(const Value: integer);
    procedure WriteString(const Value: string);
  end;

{ procedural }

function Compare(const A, B: TDatenItem): boolean;
begin
  Result := (A.Id = B.Id) and (A.Name = B.Name);
end;

{ TDaten }

destructor TDaten.Destroy;
begin
  FOnChange := nil;
  FOnChanging := nil;
  Setlength(FItems, 0);
end;

procedure TDaten.Changed;
begin
  if FSorted then
    Sort;
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TDaten.Changing;
begin
  if Assigned(FOnChanging) then
    FOnChanging(Self);
end;

procedure TDaten.Clear;
begin
  if Count <> 0 then
  begin
    Changing;
    SetLength(FItems, 0);
    Changed;
  end;
end;

procedure TDaten.Add(const Value: TDatenItem);
begin
  Insert(Count, Value);
end;

procedure TDaten.Delete(Index: integer);
var
  I: integer;
begin
  Changing;
  for I := Index to Count - 2 do
    FItems[I] := FItems[I + 1];
  SetCount(Count - 1);
  Changed;
end;

procedure TDaten.Exchange(Index1, Index2: integer);
var
  Temp: TDatenItem;
begin
  Changing;
  Temp := FItems[Index1];
  FItems[Index1] := FItems[Index2];
  FItems[Index2] := Temp;
  Changed;
end;

function TDaten.Get(Index: integer): TDatenItem;
begin
  Result := FItems[Index];
end;

function TDaten.GetCount: integer;
begin
  Result := Length(FItems)
end;

procedure TDaten.Insert(Index: integer; const Value: TDatenItem);
var
  I: integer;
begin
  Changing;
  SetCount(Count + 1);
  for I := Count - 1 downto Index + 1 do
    FItems[I] := FItems[I - 1];
  FItems[Index] := Value;
  Changed;
end;

procedure TDaten.Put(Index: integer; const Value: TDatenItem);
begin
  if not Compare(Value, FItems[Index]) then
  begin
    Changing;
    FItems[Index] := Value;
    Changed;
  end;
end;

procedure TDaten.SetCount(Value: integer);
begin
  SetLength(FItems, Value);
end;

procedure TDaten.SortExchange(Index1, Index2: integer);
var
  Temp: TDatenItem;
begin
  Temp := FItems[Index1];
  FItems[Index1] := FItems[Index2];
  FItems[Index2] := Temp;
end;

function TDaten.SortCompare(const A, B: TDatenItem): integer;
begin
  if FSortFlag = dsfName then
    Result := AnsiCompareText(A.Name, B.Name)
  else
    Result := CompareValue(A.Id, B.Id);
end;

procedure TDaten.QuickSort(L, R: integer);
var
  I, J, K: integer;
  Pivot: TDatenItem;
begin
  repeat
    I := L;
    J := R;
    K := (L + R) shr 1;
    Pivot := FItems[K];
    repeat
      while SortCompare(FItems[I], Pivot) < 0 do
        Inc(I);
      while SortCompare(FItems[J], Pivot) > 0 do
        Dec(J);
      if I <= J then
      begin
        SortExchange(I, J);
        Inc(I);
        Dec(J);
      end;
    until I > J;
    if L < J then
      QuickSort(L, J);
    L := I;
  until I >= R;
end;

procedure TDaten.Sort;
begin
  if Count > 1 then
    QuickSort(0, Count - 1);
end;

procedure TDaten.SetSorted(const Value: boolean);
begin
  if FSorted <> Value then
  begin
    Changing;
    FSorted := Value;
    Changed;
  end;
end;

function TDaten.IndexOfID(Value: integer): integer;
var
  I: integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
    if FItems[I].Id = Value then
    begin
      Result := I;
      Break;
    end;
end;

procedure TDaten.Assign(Value: TDaten);
var
  I: integer;
begin
  Clear;
  for I := 0 to Value.Count - 1 do
    Add(Value[I]);
end;

procedure TDaten.LoadFromFile(const FileName: string);
var
  Stream: TFileStreamEx;
  I, N: integer;
  Value: TDatenItem;
begin
  if FileExists(FileName) then
  begin
    Clear;
    Stream := TFileStreamEx.Create(FileName, fmOpenRead or fmShareDenyNone);
    try
      N := Stream.ReadInteger;
      for I := 0 to N - 1 do
      begin
        Value.Id := Stream.ReadInteger;
        Value.Name := Stream.ReadString;
        Add(Value);
      end;
    finally
      Stream.Free;
    end;
  end;
end;

procedure TDaten.SaveToFile(const FileName: string);
var
  Stream: TFileStreamEx;
  I: integer;
begin
  Stream := TFileStreamEx.Create(FileName, fmCreate);
  try
    Stream.WriteInteger(Count);
    for I := 0 to Count - 1 do
    begin
      Stream.WriteInteger(FItems[I].Id);
      Stream.WriteString(FItems[I].Name);
    end;
  finally
    Stream.Free;
  end;
end;

{ TFileStreamEx }

function TFileStreamEx.ReadInteger: integer;
begin
  ReadBuffer(Result, SizeOf(integer));
end;

function TFileStreamEx.ReadAnsiString: AnsiString;
var
  N: integer;
begin
  N := ReadInteger;
  SetLength(Result, N);
  if N > 0 then
    ReadBuffer(Result[1], N);
end;

function TFileStreamEx.ReadString: string; // need AnsiStringBuffer
begin
  Result := ReadAnsiString;
end;

procedure TFileStreamEx.WriteInteger(const Value: integer);
begin
  WriteBuffer(Value, SizeOf(integer));
end;

procedure TFileStreamEx.WriteString(const Value: string);
var
  N: integer;
begin
  N := Length(Value) * SizeOf(Char);
  WriteInteger(N);
  if N > 0 then
    WriteBuffer(Value[1], N);
end;

end.
  Mit Zitat antworten Zitat
Benutzerbild von Ultimator
Ultimator

Registriert seit: 17. Feb 2004
Ort: Coburg
1.860 Beiträge
 
FreePascal / Lazarus
 
#7

AW: Feld sortieren und damit weiterarbeiten

  Alt 2. Apr 2015, 09:16
Hallo zusammen,

Danke an alle! Ein bisschen aufwendiger ist es ja schon, als mir zu merken, wann ich die Daten sortiert hab
Ich werde mal in mich gehen - das Prinzip hab ich verstanden, jetzt muss ich mal schauen, wie ich's umsetze.
Julian J. Pracht
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#8

AW: Feld sortieren und damit weiterarbeiten

  Alt 2. Apr 2015, 10:53
Ja, ist ziemlich aufwendig. Am besten wäre es vielleicht, das Sort aus der Changed
herauszunehmen und vom Event erledigen zu lassen. Man könnte noch einen Setter
SetSortFlag (ala SetSorted) einführen und im destructor hab ich das das inherited vergessen.
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:26 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