ジェネリックスを使い始めたばかりですが、現在、複数のフィールドで並べ替えを行う際に問題が発生しています。
ケース:
PeopleListをとして持っていて、TObjectList<TPerson>
一度に1つの並べ替えフィールドを選択することで、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
これを行うために、2つの比較子を作成しました。1つはTLastNameComparerで、もう1つはTAgeComparerです。
私は今電話します
PeopleList.Sort(LastNameComparer)
PeopleList.Sort(AgeComparer)
今私の問題は、これが私が望む出力を生成しないということですが、
Lastname ? Age ?
---------------------
Jones 24
Smith 26
Jones 26
Lincoln 34
代わりに、Smith、26がJones、26の前に表示されます。したがって、以前の並べ替えが保持されていないようです。
LastNameとAgeの両方を比較する比較子を1つだけ作成できることは知っていますが、問題は、TPersonに存在するフィールドの組み合わせごとに比較子を作成する必要があることです。
複数のTComparerを使用してやりたいことを実行することは可能ですか、またはどうすればやりたいことを達成できますか?
新年の更新
将来の訪問者のために、これは(ほぼ)私が現在使用しているコードです。
まず、基本クラスTSortCriterion<T>
とを作成TSortCriteriaComparer<T>
して、将来的に複数のクラスで使用できるようにしました。オブジェクトリストが基準の破棄を自動的に処理する方が簡単であることがわかったので、基準とリストをそれぞれとに変更しTObject
ました。TObjectList
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;