Results 1 to 9 of 9

Thread: record sorteren

  1. #1

    record sorteren

    Hallo allemaal,

    ik heb een record (even een beetje abstract maar goed) in de vorm
    Code:
    Tgegevens =  record
     A:integer;
     B:integer;
     C:integer;
     data:string;
    end;
    Deze data voeg ik random toe aan tlist van dit type
    Deze lijst wil ik sorteren.
    Eerst A dan B en dan C

    135-1-1
    135-1-2
    135-2-1
    135-2-2
    136-1-1
    136-1-2
    136-1-3
    136-2-1
    136-2-2
    136-2-3
    enz..

    Vanuit een dataset is dit geen probleem natuurlijk (wellicht moet ik het wel als een dataset invoeren?)
    Maar voor dat ik al mijn sort routines weer moet gaan afstoffen; wellicht zijn er betere methodes om dit te doen.

    Groet

  2. #2
    Nee, gewoon een custom sort gebruiken, toch?
    1+1=b

  3. #3
    Nou ja, gewoon...

    Met een generic TList<> moet je een comparer interface meegeven. Gelukkig is dat ook vrij simpel:

    Delphi Code:
    1. TGegevensComparer = class(TInterfacedObject, IComparer<TGegevens>)
    2.     private function Compare(const Left, Right: TGegevens): Integer;
    3.   end;
    4.  
    5. { TGegevensComparer }
    6.  
    7. function TGegevensComparer.Compare(const Left, Right: TGegevens): Integer;
    8. begin
    9.   Result := Left.A - Right.A;
    10.   if Result <> 0 then Exit;
    11.   Result := Left.B - Right.B;
    12.   if Result <> 0 then Exit;
    13.   Result := Left.C - Right.C;
    14.   if Result <> 0 then Exit;
    15.   Result := CompareText(Left.data, Right.data); // indien wenselijk
    16. end;

    Maar dan komt er nog een valkuiltje. De verleiding is groot om het zo aan te roepen:

    Delphi Code:
    1. Lijst.Sort(TGegevensComparer.Create);
    Helaas heeft Delphi een 'optimalisatie', die ervoor zorgt dat er geen AddRef aangeroepen wordt, als je op deze manier een class instantieert, en die in de method-aanroep impliciet naar interface typecast. Dit levert dus een memory leak op, omdat de TGegevensComparer nooit vrijgegeven wordt.

    Je moet de gemaakte comparer dus expliciet typecasten naar interface om de AddRef te forceren. Dat betekent: eerst toekennen aan een variabele van het type `IComparer<TGegevens>`, of expliciet typecasten in de aanroep.

    Delphi Code:
    1. Lijst.Sort((TGegevensComparer.Create) as IComparer<TGegevens>);

    Mocht je 'gewoon' een inline function mee willen geven, dan heb je ook pech. TList heeft zelf geen overload waarmee dat kan. Dat is uitbesteed aan een implementatie van ICompare<T>: de TDelegatedComparer<>. Die heeft een constructor waaraan je de comparer function (al dan niet inline) mee kan geven.
    Maar die heeft dus ook hetzelfde typecast issue, dus de aanroep wordt dan zo, verre van elegant:

    Delphi Code:
    1. Lijst.Sort(
    2.    ( TDelegatedComparer<TGegevens>.Create(
    3.         function (const Left, Right: TGegevens): Integer
    4.         begin
    5.           Result := Left.A - Right.A;
    6.           if Result <> 0 then Exit;
    7.           Result := Left.B - Right.B;
    8.           if Result <> 0 then Exit;
    9.           Result := Left.C - Right.C;
    10.           if Result <> 0 then Exit;
    11.           Result := CompareText(Left.data, Right.data);
    12.         end
    13.    )) as IComparer<TGegevens>);
    1+1=b

  4. #4
    Thanks voor het antwoord.
    Ga eens kijken of ik dit zo kan implementeren.
    Maar het ziet er vrij ingewikkeld uit.

  5. #5
    Mhhh het werkt nog niet.

    Bij het compileren loop ik er tegen aan dat IComparer<TGegevens> niet gedefinieerd is.
    Voor zover ik kan zien is dit de interface van de Comparer.
    Helaas heb ik weinig kaas gegeten van interfaces.

    Dus als iemand mij aan kan geven hoe ik dit doe dan hoor ik dat graag

  6. #6
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    cpri, laat zien wat je hebt. Let op het eerste fragment van Goleztrol

  7. #7
    Het ging dus al mis bij de declaratie van
    Code:
    TGegevensComparer = class(TInterfacedObject, IComparer<TGegevens>)
        private function Compare(const Left, Right: TGegevens): Integer;
      end;
    Hierbij kreeg ik de compiler foutmelding dat IComparer niet gedefinieerd was

    Het idee was om eerst een gesorteerde lijst te maken en aan de hand daarvan mijn objecten aan te maken.
    Nu dat blijkbaar zo lastig is ben ik aan het kijken om direct mijn objecten aan te maken en deze middels add en insert de data in de juiste volgorde te zetten

  8. #8
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    System.Generics.defaults in de uses clause stoppen. Maar een simpele F1 op de declaratie had je dat ook verteld.

  9. #9
    Helaas geeft F1 bij een foutmelding (daar moet ik dus echt even naar kijken)
    Inmiddels compileert wel alles

    Ik kan nu mijn gegevens inlezen (komen uit een zip bestand) en sorteren middels de volgende code
    Code:
    opendialog1.Filter:='Zip files | *.zip';
    if opendialog1.Execute then
    begin
     ZipFile := TZipFile.Create; //Zipfile: TZipFile
     try
      ZipFile.Open(Opendialog1.FileName, zmRead);
      Gezangen:=TList<TKerkGezang>.create;
      for I := 0 to ZipFile.FileCount - 1 do
      begin
       if pos('.png',ZipFile.FileNames[i])>0 then
       begin
        TempGezang.LiedNr:=GetLiedNr(ZipFile.FileNames[I]);
        TempGezang.Vers:=GetVers(ZipFile.FileNames[I]);
        TempGezang.Volgnr:=GetVolgNr(ZipFile.FileNames[I]);
        TempGezang.FileName:=Filelist[i];
        Gezangen.Add(TempGezang);
       end;
    //    ZipFile.ExtractAll()
      end;
      ZipFile.Close;
     finally
      ZipFile.Free;
     end;
    end;
    
    Gezangen.Sort((TDelegatedComparer<TKerkGezang>.Create(
            function (const Left, Right: TKerkGezang): Integer
            begin
              Result := Left.LiedNr - Right.LiedNr;
              if Result <> 0 then Exit;
              Result := Left.Vers - Right.Vers;
              if Result <> 0 then Exit;
              Result := Left.Volgnr - Right.Volgnr;
              if Result <> 0 then Exit;
            end)) as IComparer<TkerkGezang>);
    
    
    for i := 0 to Gezangen.count-1 do
    begin
     showmessage(Gezangen[i].LiedNr.ToString + ' '+Gezangen[i].Vers.ToString +  ' '+Gezangen[i].Volgnr.ToString + ' ' + Gezangen[i].FileName );
    end
    Alles staat dus nu keurig op volgorde.
    Kan nu zelfs mijn hele objecten structuur die voor ogen had weg doen en met een paar slimme if statements deze lijst gebruiken.

    Super!!!

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •