【发布时间】:2021-11-29 18:07:53
【问题描述】:
查看示例 How to make an Excel-Like Sort By A, Then By B in a TObjectList<> using multiple comparers 我已经构建了以下测试
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.Generics.Defaults,
System.Generics.Collections,
System.Contnrs,
System.SysUtils;
type
TPerson = class(TObject)
age: Integer;
name: string;
public
constructor Create(aAge: Integer; aName: string); reintroduce;
end;
TSortCriterion<T> = class(TObject)
Ascending: Boolean;
Comparer: IComparer<T>;
end;
TSortCriteriaComparer<T> = class(TComparer<T>)
private
SortCriteria: TObjectList<TSortCriterion<T>>;
public
constructor Create;
destructor Destroy; override;
function Compare(const Right, Left: T): Integer; override;
procedure ClearCriteria; virtual;
procedure AddCriterion(NewCriterion: TSortCriterion<T>); virtual;
end;
TPersonAgeComparer = class(TComparer<TPerson>)
public
function Compare(const Left, Right: TPerson): Integer; override;
end;
TPersonLastNameComparer = class(TComparer<TPerson>)
public
function Compare(const Left, Right: TPerson): Integer; override;
end;
{ TSortCriteriaComparer<T> }
procedure TSortCriteriaComparer<T>.AddCriterion(NewCriterion: TSortCriterion<T>);
begin
SortCriteria.Add(NewCriterion);
end;
procedure TSortCriteriaComparer<T>.ClearCriteria;
begin
SortCriteria.Clear;
end;
function TSortCriteriaComparer<T>.Compare(const Right, Left: T): Integer;
var
Criterion: TSortCriterion<T>;
begin
for Criterion in SortCriteria do
begin
Result := Criterion.Comparer.Compare(Right, Left);
if not Criterion.Ascending then
Result := -Result;
if Result <> 0 then
Exit;
end;
end;
constructor TSortCriteriaComparer<T>.Create;
begin
inherited;
SortCriteria := TObjectList<TSortCriterion<T>>.Create(True);
end;
destructor TSortCriteriaComparer<T>.Destroy;
begin
SortCriteria.Free;
inherited;
end;
{ TPersonAgeComparer }
function TPersonAgeComparer.Compare(const Left, Right: TPerson): Integer;
begin
if Left.age > Right.age then
Result := -1
else if Left.age < Right.age then
result := 1
else
result := 0;
end;
{ TPersonLastNameComparer }
function TPersonLastNameComparer.Compare(const Left, Right: TPerson): Integer;
begin
if Left.name > Right.name then
Result := -1
else if Left.name < Right.name then
result := 1
else
result := 0;
end;
{ TPerson }
constructor TPerson.Create(aAge: Integer; aName: string);
begin
Age := aAge;
name := aName;
end;
var
PersonComparer: TSortCriteriaComparer<TPerson>;
Criterion: TSortCriterion<TPerson>;
PeopleList: TObjectList<TPerson>;
Person: TPerson;
begin
PersonComparer := TSortCriteriaComparer<TPerson>.Create;
try
Criterion := TSortCriterion<TPerson>.Create;
Criterion.Ascending := True;
Criterion.Comparer := TPersonAgeComparer.Create;
PersonComparer.AddCriterion(Criterion);
Criterion := TSortCriterion<TPerson>.Create;
Criterion.Ascending := True;
Criterion.Comparer := TPersonLastNameComparer.Create;
PersonComparer.AddCriterion(Criterion);
PeopleList := TObjectList<TPerson>.Create;
PeopleList.Sort(PersonComparer);
Person := TPerson.Create(26, 'Smith');
PeopleList.Add(Person);
Person := TPerson.Create(26, 'Jones');
PeopleList.Add(Person);
Person := TPerson.Create(24, 'Jones');
PeopleList.Add(Person);
Person := TPerson.Create(34, 'Lincoln');
PeopleList.Add(Person);
finally
PersonComparer.Free;
end;
end.
但不是给出以下结果
Lastname ▲ Age ▲
---------------------
Jones 24
Jones 26
Smith 26
Lincoln 34
我有这个
Lastname ▲ Age ▲
---------------------
Smith 26
Jones 26
Jones 24
Lincoln 34
我尝试更改TPersonLastNameComparer.Comparer 和TPersonAgeComparer.Comparer,但我无法获得理想的结果。
我做错了什么?
【问题讨论】:
-
当列表为空时排序,然后填充它。先填充然后排序。另外,我认为您以错误的顺序添加标准。
-
@DavidHeffernan 非常感谢大卫。我的印象是 TObjectList
.Sort 应该在开头设置,并且列表会自动排序。考虑到它更有意义,必须在需要时对 TObjectList 进行排序。你在比较器上也是对的,应该是相反的。