Я только начал использовать generics, и в настоящее время у меня проблема с сортировкой по нескольким полям.
Case:
У меня есть PeopleList как TObjectList<TPerson>
, и я хочу, чтобы иметь возможность сортировки по Excel, путем выбора одного поля сортировки за раз, но сохраняя предыдущую сортировку как можно больше.
EDIT: Должна быть возможность изменить последовательность сортировки полей во время выполнения. (То есть в одном сценарии пользователь хочет, чтобы порядок сортировки 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
Чтобы сделать это, я сделал два Comparers - один TLastNameComparer и один TAgeComparer.
Теперь я вызываю
PeopleList.Sort(LastNameComparer)
PeopleList.Sort(AgeComparer)
Теперь моя проблема в том, что это не дает результат, который я хочу, но
Lastname ? Age ?
---------------------
Jones 24
Smith 26
Jones 26
Lincoln 34
где Смит, 26 появляется перед Джонсом, 26. Поэтому кажется, что он не сохраняет предыдущую сортировку.
Я знаю, что могу сделать только один сравнитель, который сравнивает как LastName, так и Age - но проблема в том, что мне тогда приходится делать сравнения для каждой комбинации полей, присутствующих в TPerson.
Можно ли делать то, что я хочу, используя несколько TComparers или как я могу выполнить то, что хочу?
Новогоднее обновление
Просто для ссылки на будущих посетителей, это (почти) код, который я использую сейчас.
Сначала я создал базовый класс TSortCriterion<T>
и a 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;