В данной статье мы рассмотрим вопрос сортировки объектов в TObjectList с использованием собственной функции сравнения, которая поддерживает как различия, так и равенство между объектами.
Описание проблемы
Разработчик хочет отсортировать TObjectList с использованием своей функции сравнения. Функция сравнения принимает два параметра (S1 и S2) и использует SysUtil.CompareText для сравнения. Для сортировки в обратном порядке разработчик меняет знак результата функции CompareText. Однако, когда S1 и S2 равны, список не переиндексируется, так как все элементы в столбце идентичны. При сортировке в обратном порядке список сортируется так, как будто S1 > S2 или S1 < S2.
Вопрос
Как реализовать функцию сравнения, которая поддерживает как равенство, так и различия между объектами?
Пример кода
Ниже приведен пример кода, демонстрирующий использование TObjectList и сортировку по собственной функции сравнения:
type
TPerson = class
private
FName: string;
FId: string;
public
property Name: string read FName write FName;
property ID: string read FID write FID;
end;
TPersons = class(TObjectList<TPerson>)
public
constructor Create;
procedure Sort(Direction: string); reintroduce;
end;
procedure TForm4.Button1Click(Sender: TObject);
var
PersonsList: TPersons;
I: Integer;
begin
PersonsList := TPersons.Create;
PersonsList.Sort('Ascending');
for I := 0 to PersonsList.Count - 1 do
ShowMessage(PersonsList[i].Name);
end;
{ TPersons }
constructor TPersons.Create;
var
Person: TPerson;
begin
Person := TPerson.Create;
Person.Name := 'fateh';
Person.ID := '1';
Self.Add(Person);
Person := TPerson.Create;
Person.Name := 'mohamed';
Person.ID := '1';
Self.Add(Person);
Person := TPerson.Create;
Person.Name := 'oussama';
Person.ID := '1';
Self.Add(Person);
// все идентификаторы одинаковы
end;
procedure TPersons.Sort(Direction: string);
var
Comparer: IComparer<TPerson>;
Comparison: TComparison<TPerson>;
begin
if Direction = 'Ascending' then
begin
Comparison := function(const Person1, Person2: TPerson): Integer
begin
result := CompareText(Person1.ID, Person2.ID);
end;
end;
if Direction = 'Descending' then
begin
Comparison := function(const Person1, Person2: TPerson): Integer
begin
result := -CompareText(Person1.ID, Person2.ID);
end;
end;
Comparer := TComparer<TPerson>.Construct(Comparison);
inherited Sort(Comparer);
end;
Подтвержденный ответ
Компаратор должен возвращать нуль только в том случае, когда объекты действительно равны во всех отношениях, о которых заботится разработчик. Если объекты с одинаковыми ID все еще различаются по другим атрибутам, необходимо использовать каскадное сравнение, чтобы определить правильный порядок.
Ниже приведен пример каскадного компаратора, который сравнивает объекты по нескольким атрибутам:
Comparison := function(const Person1, Person2: TPerson): Integer
begin
Result := CompareText(Person1.ID, Person2.ID);
if Result <> 0 then
exit;
Result := CompareText(Person1.Name, Person2.Name);
if Result <> 0 then
exit;
Result := Person1.Age - Person2.Age;
if Result <> 0 then
exit;
Result := GenderCompare(Person1.Sex, Person2.Sex);
if Result <> 0 then
exit;
Result := Person1.Salary - Person2.Salary;
if Result <> 0 then
exit;
... и так далее
end;
Альтернативный ответ
Если разработчик все же хочет использовать стандартный интерфейс компаратора и стандартные контейнеры, он не должен делать никаких предположений о свойствах алгоритма сортировки, используемого контейнером. В противном случае, при смене контейнера или алгоритма сортировки поведение программы может измениться.
Заключение
Для правильной сортировки TObjectList по собственной функции сравнения с поддержкой равенства и различия необходимо создать компаратор, который возвращает нуль только в том случае, когда объекты действительно равны во всех отношениях, о которых заботится разработчик. Если объекты с одинаковыми ID все еще различаются по другим атрибутам, необходимо использовать каскадное сравнение, чтобы определить правильный порядок. При использовании стандартных интерфейсов компаратора и контейнеров не следует делать никаких предположений о свойствах алгоритма сортировки, используемого контейнером.
В этом тексте рассматривается вопрос сортировки объектов в TObjectList с использованием собственной функции сравнения, которая учитывает как различия, так и равенство между объектами.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.