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
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;