How to make an Excel-Like Sort By A, Then By B in a TObjectList<> using multiple comparers

前端 未结 3 1092
离开以前
离开以前 2021-02-01 20:23

I have just started to use generics, and I am currently having a problem doing sorting on multiple fields.

Case:
I have a PeopleList as a TObjectList

3条回答
  •  执念已碎
    2021-02-01 20:44

    Your problem is that you are performing two separate sorts. You need to perform a single sort and use what is known as a lexical ordering. You need to use a comparer that compares the primary field and then, only if the primary key compares equal, goes on to compare the secondary key. Like this:

    Result := CompareStr(Left.Name, Right.Name);
    if Result=0 then
      Result := Left.Age-Right.Age;
    

    This approach can be extended to cater for an arbitrary number of keys.


    In your update to the question you add the requirement that the key precedence will be determined at runtime. You can do this with a comparison function like this:

    function TMyClass.Comparison(const Left, Right: TPerson): Integer;
    var
      i: Integer;
    begin
      for i := low(FSortField) to high(FSortField) do begin
        Result := CompareField(Left, Right, FSortField[i]);
        if Result<>0 then begin
          exit;
        end;
      end;
    end;
    

    Here FSortField is an array containing identifiers for the fields, in descending order of precendence. So FSortField[0] identifies the primary key, FSortField[1] identifies the secondary key and so on. The CompareField function compares the field identified by its third parameter.

    So the CompareField function might be like this:

    function CompareField(const Left, Right: TPerson; Field: TField): Integer;
    begin
      case Field of
      fldName:
        Result := CompareStr(Left.Name, Right.Name);
      fldAge:
        Result := Left.Age-Right.Age;
      //etc.
      end;
    end;
    

提交回复
热议问题