Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何在对象列表中按A、B进行类似Excel的排序<&燃气轮机;使用多个比较器_Delphi_Generics_Sorting_Tobjectlist - Fatal编程技术网

Delphi 如何在对象列表中按A、B进行类似Excel的排序<&燃气轮机;使用多个比较器

Delphi 如何在对象列表中按A、B进行类似Excel的排序<&燃气轮机;使用多个比较器,delphi,generics,sorting,tobjectlist,Delphi,Generics,Sorting,Tobjectlist,我刚刚开始使用泛型,目前在对多个字段进行排序时遇到问题 案例: 我有一个PeopleList作为TObjectList,我想通过一次选择一个排序字段,但尽可能保留以前的排序,来实现类似Excel的排序功能 编辑:必须能够在运行时更改字段排序顺序。(即,在一个场景中,用户需要排序顺序A、B、C-在另一个场景中,他需要B、A、C-在另一个A、C、D中) 假设我们有一份未分类的人员名单: Lastname Age --------------------- Smith 26 Jo

我刚刚开始使用泛型,目前在对多个字段进行排序时遇到问题

案例:
我有一个PeopleList作为
TObjectList
,我想通过一次选择一个排序字段,但尽可能保留以前的排序,来实现类似Excel的排序功能

编辑:必须能够在运行时更改字段排序顺序。(即,在一个场景中,用户需要排序顺序A、B、C-在另一个场景中,他需要B、A、C-在另一个A、C、D中)

假设我们有一份未分类的人员名单:

Lastname     Age
---------------------
Smith        26
Jones        26
Jones        24
Lincoln      34
现在,如果我按姓氏排序:

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岁的史密斯出现在26岁的琼斯面前。所以它似乎没有保留以前的排序

我知道我只能制作一个比较LastName和Age的比较器,但问题是,我必须为TPerson中存在的每个字段组合制作比较器

是否可以使用多个t比较程序来实现我的目标,或者如何实现我的目标

新年更新 仅供将来的访问者参考,这是(几乎)我现在使用的代码

首先,我创建了一个基类
tsortcriteria
和一个
TSortCriteriaComparer
,以便将来能够在多个类中使用它们。 我已将标准和列表分别更改为
TObject
TObjectList
,因为我发现如果objectlist自动处理标准的销毁,则更容易

  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;
tsortcriteria=Class(TObject)
升序:布尔;
比较器:IComparer;
结束;
TSortCriteriaComparer=类(TComparer)
私有的
分类标准:TObjectList;
公开的
构造函数创建;
毁灭者毁灭;推翻
函数比较(Const Right,Left:T):整数;推翻
程序标准;事实上的
程序添加标准(新标准:TSORTcriteria);事实上的
结束;
实施
{TSortCriteriaComparer}
程序TSortCriteriaComparer.addCriteria(新标准:TsortCriteria);
开始
添加(新标准);
结束;
程序TSortCriteriaComparer.ClearCriteria;
开始
SortCriteria.Clear;
结束;
函数TSortCriteriaComparer.Compare(Const Right,Left:T):整数;
变量
标准:TSORT标准;
开始
对于SortCriteria中的标准,请开始
结果:=标准.比较器.比较(右,左);
如果不是标准,那么上升
结果:=-结果;
如果结果为0,则
出口
结束;
结束;
构造函数TSortCriteriaComparer.Create;
开始
继承;
SortCriteria:=TObjectList.Create(True);
结束;
析构函数TSortCriteriaComparer.Destroy;
开始
免费;
继承;
结束;
最后,为了使用排序标准: (这只是为了示例,因为创建排序顺序的逻辑实际上取决于应用程序):

程序TForm1.SortList;
变量
个人比较:标准比较器;
标准:TSORT标准;
开始
PersonComparer:=TSortCriteriaComparer.Create;
尝试
条件:=tsortcriteria.Create;
标准。升序:=真;
criteria.Comparer:=TPersonAgeComparer.Create
人员比较。添加标准(标准);
条件:=tsortcriteria.Create;
标准。升序:=真;
criteria.Comparer:=TPersonLastNameComparer.Create
人员比较。添加标准(标准);
PeopleList.Sort(PersonComparer);
//对有序的人员列表进行处理。
最后
个人比较。免费;
结束;
结束;

您的问题是您正在执行两种不同的排序。您需要执行单个排序并使用所谓的词法排序。您需要使用比较主字段的比较器,然后,仅当主键比较相等时,才继续比较次键。像这样:

Result := CompareStr(Left.Name, Right.Name);
if Result=0 then
  Result := Left.Age-Right.Age;
这种方法可以扩展到满足任意数量的密钥


在问题的更新中,您添加了将在运行时确定密钥优先级的要求。您可以使用如下比较函数来完成此操作:

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;
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;
function Compare(const A, B: TPerson): Integer;
var
  Criterion: TSortCriterion<TPerson>;
begin
  for Criterion in SortCriteria do begin
    Result := Criterion.Comparer.Compare(A, B);
    if not Criterion.Ascending then
      Result := -Result;
    if Result <> 0 then
      Exit;
  end;
end;

如果您有一个稳定的排序算法,那么您可以按相反的顺序应用每个比较器,结果将是一个按所需顺序排序的列表。Delphi的列表类使用快速排序,这不是一种稳定的排序。您需要应用自己的排序例程,而不是内置的排序例程。

将您的排序条件放入一个列表中,其中包括排序方向和用于比较项目的函数。这样的记录可能会有所帮助:

type
  TSortCriterion<T> = record
    Ascending: Boolean;
    Comparer: IComparer<T>;
  end;

谢谢你的快速回答,大卫。问题是,我希望用户能够在运行时更改排序顺序,通过使用这种方法,我必须为每个字段组合创建一个比较器,例如,如果用户在某个时间希望先按年龄排序,然后按姓氏排序,在另一种情况下,他希望按姓氏排序,然后是年龄-然后我需要两个比较器来做这个。想象一下,如果还可以使用字段排序序列的任意组合按FirstName、PhoneNo、ShoeSize等进行排序,那么我需要多少个比较器。@Techno您只需要一个比较器和一点间接寻址。请看我的更新。这就是我需要的提示,Robs完美抛光的版本使它变得完美。谢谢你的时间帮助。看,应该会给你一些想法。+1这是一个很好的说明,我喜欢在一个很好的封装记录中包含升序/降序。@Rob:这正是我所需要的,正如David提到的,升序/降序功能的很好的使用。感谢您的帮助。Delphi是否有稳定的排序算法(无论Delphi的列表类是否使用)
var
  SortCriteria: TList<TSortCriterion>;
function Compare(const A, B: TPerson): Integer;
var
  Criterion: TSortCriterion<TPerson>;
begin
  for Criterion in SortCriteria do begin
    Result := Criterion.Comparer.Compare(A, B);
    if not Criterion.Ascending then
      Result := -Result;
    if Result <> 0 then
      Exit;
  end;
end;