How do I sort a generic list using a custom comparer?

后端 未结 4 1695
谎友^
谎友^ 2020-11-30 00:08

I\'m kinda a Delphi-newbie and I don\'t get how the Sort method of a TList of Records is called in order to sort the records by ascending integer value. I have a record like

相关标签:
4条回答
  • 2020-11-30 00:20

    I found a much simpler modified sort function to alphabetize a TList of records or nonstandard list of items.

    Example

    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;
    

    and the function to call to sort your list

    Contacts.Sort(@CompareNames);
    
    0 讨论(0)
  • 2020-11-30 00:21

    The concise answer:

    uses
      .. System.Generics.Defaults // Contains TComparer
    
    myList.Sort(
      TComparer<TMyRecord>.Construct(
        function(const Left, Right: TMyRecord): Integer
        begin
          Result := Left.intVal - Right.intVal;
        end
      )
    );
    
    0 讨论(0)
  • 2020-11-30 00:22

    I want to share my solution (based on the input I have gathered here).

    It's a standard setup. A filedata class that holds data of a single file in a generic TObjectList. The list has the two private attributes fCurrentSortedColumn and fCurrentSortAscending to control the sort order. The AsString-method is the path and filename combined.

    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 讨论(0)
  • 2020-11-30 00:40

    The Sort overload you should be using is this one:

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

    Now, you can create an IComparer<TMyRecord> by calling TComparer<TMyRecord>.Construct. Like this:

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

    I've written the Comparison function as an anonymous method, but you could also use a plain old style non-OOP function, or a method of an object.

    One potential problem with your comparison function is that you may suffer from integer overflow. So you could instead use the default integer comparer.

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

    It might be expensive to call TComparer<Integer>.Default repeatedly so you could store it away in a global variable:

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

    Another option to consider is to pass in the comparer when you create the list. If you only ever sort the list using this ordering then that's more convenient.

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

    And then you can sort the list with

    List.Sort;
    
    0 讨论(0)
提交回复
热议问题