情况如下:
我有一个PeopleList作为TObjectList,我希望能够制作类似Excel的排序功能,每次选择一个排序字段,但尽可能保留以前的排序顺序。
编辑:必须可以在运行时更改字段排序顺序。(即,在一个场景中,用户想要排序顺序A、B、C,在另一个场景中,他想要B、A、C,在另一个场景中是A、C、D)
假设我们有一个未排序的人员列表:
Lastname Age
---------------------
Smith 26
Jones 26
Jones 24
Lincoln 34
现在,如果我按LastName排序:
Lastname ▲ Age
---------------------
Jones 26
Jones 24
Lincoln 34
Smith 26
如果我按年龄排序,我想要这样:
Lastname ▲ Age ▲
---------------------
Jones 24
Jones 26
Smith 26
Lincoln 34
为了实现这个目的,我创建了两个比较器——一个TLastNameComparer和一个TAgeComparer。
现在我调用:
PeopleList.Sort(LastNameComparer)
PeopleList.Sort(AgeComparer)
现在我的问题是,这并没有产生我想要的输出,但是。
Lastname ? Age ?
---------------------
Jones 24
Smith 26
Jones 26
Lincoln 34
在排序中,26岁的Smith先于26岁的Jones出现。因此,似乎没有保留之前的排序。
我知道可以只创建一个比较器,来比较LastName和Age两个字段 - 但是问题是,这样我就需要为TPerson中每个字段组合创建不同的比较器。
是否可以使用多个TComparers来实现我的需求?或者我应该如何完成我想要的操作呢?
新年更新
仅供将来的访客参考,这几乎是我现在使用的代码。
首先,我创建了一个基类TSortCriterion<T>
和TSortCriteriaComparer<T>
,以便今后能够在多个类中使用它们。
我已经将Criterion和list更改为TObject
和TObjectList
,因为我发现如果对象列表自动处理Criterion的销毁,那么更容易管理。
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;
implementation
{ 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;
最后,为了使用排序标准: (这只是一个例子,因为创建排序顺序的逻辑真正取决于应用程序):
Procedure TForm1.SortList;
Var
PersonComparer : TSortCriteriaComparer<TPerson>;
Criterion : TSortCriterion<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.Sort(PersonComparer);
// Do something with the ordered list of people.
Finally
PersonComparer.Free;
End;
End;