web-dev-qa-db-fra.com

Comment trier une liste générique à l'aide d'un comparateur personnalisé?

Je suis un peu un débutant dans Delphi et je ne comprends pas comment la méthode de tri d'une TList of Records est appelée afin de trier les enregistrements par la valeur entière croissante .

 type
   TMyRecord = record
     str1: string;
     str2: string;
     intVal: integer;
   end;

Et une liste générique de tels enregistrements:

TListMyRecord = TList<TMyRecord>;

J'ai essayé de trouver un exemple de code dans les fichiers d'aide et j'ai trouvé celui-ci:

MyList.Sort(@CompareNames);

Ce que je ne peux pas utiliser, car il utilise des classes. J'ai donc essayé d'écrire ma propre fonction de comparaison avec un peu de paramètres différents:

function CompareIntVal(i1, i2: TMyRecord): Integer;
begin
  Result := i1.intVal - i2.intVal;
end;

Mais le compilateur lance toujours un 'pas assez de paramètres' - une erreur lorsque je l'appelle avec open.Sort(CompareIntVal);, ce qui semble évident; alors j'ai essayé de rester plus près du fichier d'aide: 

function SortKB(Item1, Item2: Pointer): Integer;
begin
  Result:=PMyRecord(Item1)^.intVal - PMyRecord(Item2)^.intVal;
end;

avec PMyRecord en tant que PMyRecord = ^TMyRecord;

J'ai essayé différentes manières d'appeler une fonction, en obtenant toujours des erreurs ...

31
p1.e

La surcharge Sort que vous devriez utiliser est la suivante:

procedure Sort(const AComparer: IComparer<TMyRecord>);

Maintenant, vous pouvez créer un IComparer<TMyRecord> en appelant TComparer<TMyRecord>.Construct. Comme ça:

var
  Comparison: TComparison<TMyRecord>;
....
Comparison := 
  function(const Left, Right: TMyRecord): Integer
  begin
    Result := Left.intVal-Right.intVal;
  end;
List.Sort(TComparer<TMyRecord>.Construct(Comparison));

J'ai écrit la fonction Comparison en tant que méthode anonyme, mais vous pouvez également utiliser une fonction non-OOP de style ancien ou une méthode d'objet.

Un problème potentiel avec votre fonction de comparaison est que vous pouvez souffrir d'un dépassement d'entier. Vous pouvez donc utiliser le comparateur d'entiers par défaut.

Comparison := 
  function(const Left, Right: TMyRecord): Integer
  begin
    Result := TComparer<Integer>.Default.Compare(Left.intVal, Right.intVal);
  end;

Il peut être coûteux d'appeler plusieurs fois le TComparer<Integer>.Default pour pouvoir le stocker dans une variable globale:

var
  IntegerComparer: IComparer<Integer>;
....
initialization
  IntegerComparer := TComparer<Integer>.Default;

Une autre option à considérer consiste à transmettre le comparateur lorsque vous créez la liste. Si vous ne triez jamais la liste en utilisant cet ordre, c'est plus pratique.

List := TList<TMyRecord>.Create(TComparer<TMyRecord>.Construct(Comparison));

Et puis vous pouvez trier la liste avec

List.Sort;
46
David Heffernan

La réponse concise:

uses
  .. System.Generics.Defaults // Contains TComparer

myList.Sort(
  TComparer<TMyRecord>.Construct(
    function(const Left, Right: TMyRecord): Integer
    begin
      Result := Left.intVal - Right.intVal;
    end
  )
);
4
Ian Boyd

J'ai trouvé une fonction de tri modifiée beaucoup plus simple pour alphabétiser une liste d'enregistrements ou une liste non standard d'éléments.

Exemple

PList = ^TContact;
    TContact = record             //Record for database of user contact records
      firstname1 : string[20];
      lastname1 : string[20];
       phonemobile : Integer;       //Fields in the database for contact info
      phonehome : Integer;
      street1 : string;
      street2 : string;

 type
    TListSortCompare = function (Item1,
                                Item2: TContact): Integer;
var
  Form1: TForm1;
  Contact : PList;         //declare record database for contacts
  arecord : TContact;
  Contacts : TList;   //List for the Array of Contacts

function CompareNames(i1, i2: TContact): Integer;
begin
   Result := CompareText(i1.lastname1, i2.lastname1) ;
end;

et la fonction à appeler pour trier votre liste

Contacts.Sort(@CompareNames);
2
Jason Brown

Je souhaite partager ma solution (sur la base des informations que j'ai rassemblées ici).

C'est une configuration standard. Une classe filedata qui contient les données d'un seul fichier dans un TObjectList générique. La liste comporte les deux attributs privés fCurrentSortedColumn et fCurrentSortAscending pour contrôler l'ordre de tri. La méthode AsString est le chemin et le nom du fichier combinés.

function TFileList.SortByColumn(aColumn: TSortByColums): boolean;
var
  Comparison: TComparison<TFileData>;
begin
  result := false;
  Comparison := nil;

  case aColumn of
    sbcUnsorted   : ;
    sbcPathAndName: begin
                      Comparison := function(const Left, Right: TFileData): integer
                                    begin
                                      Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
                                    end;
                    end;
    sbcSize       : begin
                      Comparison := function(const Left, Right: TFileData): integer
                                    begin
                                      Result := TComparer<int64>.Default.Compare(Left.Size,Right.Size);
                                      if Result = 0 then
                                        Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
                                    end;
                    end;
    sbcDate       : begin
                      Comparison := function(const Left, Right: TFileData): integer
                                    begin
                                      Result := TComparer<TDateTime>.Default.Compare(Left.Date,Right.Date);
                                      if Result = 0 then
                                        Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
                                    end;
                    end;
    sbcState      : begin
                      Comparison := function(const Left, Right: TFileData): integer
                                    begin
                                      Result := TComparer<TFileDataTestResults>.Default.Compare(Left.FileDataResult,Right.FileDataResult);
                                      if Result = 0 then
                                        Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
                                    end;
                     end;
  end;

  if assigned(Comparison) then
  begin
    Sort(TComparer<TFileData>.Construct(Comparison));

    // Control the sort order
    if fCurrentSortedColumn = aColumn then
      fCurrentSortAscending := not fCurrentSortAscending
    else begin
      fCurrentSortedColumn := aColumn;
      fCurrentSortAscending := true;
    end;

    if not fCurrentSortAscending then
      Reverse;

    result := true;
  end;
end;
0
Henrik Carlsen