0

I have been trying to sort a collection of objects. While I have seen many promising solutions, my compiler simply won’t accept them (error message in procedure GenReorderSpeciesList). I am using Delphi 11 FMX on Windows 10. I hope someone can direct me to some assistance.

uses
  System.SysUtils,
  System.Generics.Collections, System.Contnrs, System.Math,
  uIS_LSAReportsTypes;

function CompareMyObjects(Item1, Item2: TSpecies): Integer;
procedure GenReorderSpeciesList(var aSpcList : TList<TSpecies>);

implementation

{
 TSpecies = class
    SubFamID : integer;
    SubSpcID : integer;
    SPGroup : string;
    Family : string;
    Subfamily : string;
}

function CompareMyObjects(Item1, Item2: TSpecies): Integer;
begin
  // Sort by Field1 (ascending, case-insensitive)
  Result := AnsiCompareText(Item1.SPGroup, Item2.SPGroup);   // Returns a value less than 0 if S1 < S2,
                                                             // a value greater than 0 if S1 > S2,
                                                             // and 0 if S1 = S2
  if Result <> 0 then Exit;

  // If Field1 values are equal, sort by Field2 (ascending, case-insensitive)
  Result := AnsiCompareText(Item1.Family, Item2.Family);
  if Result <> 0 then Exit;

  // If Field1 and Field2 are equal, sort by Field3 (descending)
  if Item1.Subfamily < Item2.Subfamily then
    Result := 1 // Item1 comes after Item2
  else if Item1.Subfamily > Item2.Subfamily then
    Result := -1 // Item1 comes before Item2
  else
    Result := 0; // All fields are equal
end;

procedure GenReorderSpeciesList(var aSpcList : TList<TSpecies>);
begin
  aSpcList<TSpecies>.Sort(@CompareMyObjects); // [dcc64 Error] uIS_LSAReportsGenfunctions.pas(47): E2014 Statement expected, but expression of type 'Boolean' found

// ALSO produces same error
// aSpcList<TSpecies>.Sort(CompareMyObjects);     
end;

The error is:

[dcc64 Error] uIS_LSAReportsGenfunctions.pas(47): E2014 Statement expected, but expression of type 'Boolean' found

6
  • 1
    Surely you mean aSpcList.Sort(@CompareMyObjects);? Commented Nov 3 at 12:32
  • I tried that but with this result. [dcc64 Error] uIS_LSAReportsGenfunctions.pas(47): E2250 There is no overloaded version of 'Sort' that can be called with these arguments Commented Nov 3 at 15:46
  • 1
    Which is a major improvement, because now we have a semantic error, and not a syntax error. Just construct a TComparer, like in Matthias' answer. Commented Nov 3 at 16:54
  • It doesn't matter in this case if you create a FMX, VCL or console project! Commented Nov 3 at 18:23
  • The key issue here is confusing the list TList with a collection TList<>. Commented Nov 4 at 7:05

2 Answers 2

4

First, there is a typo in your code: aSpcList<TSpecies> needs to be just aSpcList. Don't specify Generic parameters on variables, only on types.

Second, whereas the non-Generic TList.Sort() method expects a raw function pointer like you are doing, the Generic TList<T>.Sort() overload that takes a custom comparator expects an IComparer<T> interface instead.

So, you can re-write CompareMyObjects to be a class that implements the ICompare<T>.Compare() method, and then you can pass an instance of that class to TList<T>.Sort(), eg:

type
  TSpeciesListComparer = class(TInterfacedObject, IComparer<TSpecies>)
    function Compare(const Left, Right: TSpecies): Integer;
  end;

...

function TSpeciesListComparer.Compare(const Left, Right: TSpecies): Integer;
begin
  Result := ...;
end;

procedure GenReorderSpeciesList(aSpcList : TList<TSpecies>);
var
  Comparer: IComparer<TSpecies>;
begin
  Comparer := TSpeciesListComparer.Create;
  aSpcList.Sort(Comparer);
end;

Alternatively, the RTL provides some default implementations of IComparer<T> that you can use, such as TDelegatedComparer, which would allow you to use your existing CompareMyObjects() function (if you can tweak its signature to add const to its parameters), eg:

function CompareMyObjects(const Item1, Item2: TSpecies): Integer;
begin
  Result := ...;
end;

procedure GenReorderSpeciesList(aSpcList : TList<TSpecies>);
var
  Comparer: IComparer<TSpecies>;
begin
  Comparer := TDelegatedComparer<TSpecies>.Create(CompareMyObjects);
  aSpcList.Sort(Comparer);
end;

If, for whatever reason, you can't tweak the signature, you can wrap it with an anonymous function instead, eg:

function CompareMyObjects(Item1, Item2: TSpecies): Integer;
begin
  Result := ...;
end;

procedure GenReorderSpeciesList(aSpcList : TList<TSpecies>);
var
  Comparer: IComparer<TSpecies>;
begin
  Comparer := TDelegatedComparer<TSpecies>.Create(
    function(const Left, Right: TSpecies): Integer
    begin
      Result := CompareMyObjects(Left, Right);
    end
  );
  aSpcList.Sort(Comparer);
end;
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much. Both solutions worked for me.
The second approach will not compile unless you add const to the CompareMyObjects parameters.
0

You can write it like this:

aSpcList.Sort(TComparer<TSpecies>.Construct(CompareMyObjects));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.