Как отсортировать общий список с помощью пользовательского сопоставления?
Я вроде как новичок Delphi, и я не понимаю, как вызывается метод Sort TList Records для сортировки записей по возрастанию целочисленного значения.
У меня есть запись, подобная следующей:
type
TMyRecord = record
str1: string;
str2: string;
intVal: integer;
end;
И общий список таких записей:
TListMyRecord = TList<TMyRecord>;
Попробовали найти код-пример в файлах справки и нашли это:
MyList.Sort(@CompareNames);
Я не могу использовать, так как он использует классы. Поэтому я попытался написать свою собственную функцию сравнения с несколькими различными параметрами:
function CompareIntVal(i1, i2: TMyRecord): Integer;
begin
Result := i1.intVal - i2.intVal;
end;
Но компилятор всегда выдает "недостаточно параметров" - ошибку, когда я вызываю его с помощью open.Sort(CompareIntVal);
, что кажется очевидным; поэтому я попытался приблизиться к файлу справки:
function SortKB(Item1, Item2: Pointer): Integer;
begin
Result:=PMyRecord(Item1)^.intVal - PMyRecord(Item2)^.intVal;
end;
с PMyRecord как PMyRecord = ^TMyRecord;
Я пробовал разные способы вызова функции, всегда получая некоторую ошибку...
Ответы
Ответ 1
Перегрузка Sort
, которую вы должны использовать, - это:
procedure Sort(const AComparer: IComparer<TMyRecord>);
Теперь вы можете создать IComparer<TMyRecord>
, вызвав TComparer<TMyRecord>.Construct
. Вот так:
var
Comparison: TComparison<TMyRecord>;
....
Comparison :=
function(const Left, Right: TMyRecord): Integer
begin
Result := Left.intVal-Right.intVal;
end;
List.Sort(TComparer<TMyRecord>.Construct(Comparison));
Я написал функцию Comparison
как анонимный метод, но вы также можете использовать обычную функцию non-OOP для старого стиля или метод объекта.
Одна потенциальная проблема с вашей функцией сравнения заключается в том, что вы можете страдать от целочисленного переполнения. Таким образом, вместо этого вы можете использовать целочисленный компаратор по умолчанию.
Comparison :=
function(const Left, Right: TMyRecord): Integer
begin
Result := TComparer<Integer>.Default.Compare(Left.intVal, Right.intVal);
end;
Возможно, было бы дорого вызывать TComparer<Integer>.Default
несколько раз, чтобы вы могли сохранить его в глобальной переменной:
var
IntegerComparer: IComparer<Integer>;
....
initialization
IntegerComparer := TComparer<Integer>.Default;
Другой вариант, который следует учитывать, - это передать в компаньон при создании списка. Если вы только сортируете список, используя этот порядок, то это более удобно.
List := TList<TMyRecord>.Create(TComparer<TMyRecord>.Construct(Comparison));
И затем вы можете отсортировать список с помощью
List.Sort;
Ответ 2
Я нашел гораздо более простую модифицированную функцию сортировки для алфавита TList записей или нестандартного списка элементов.
Пример
PList = ^TContact;
TContact = record //Record for database of user contact records
firstname1 : string[20];
lastname1 : string[20];
phonemobile : Integer; //Fields in the database for contact info
phonehome : Integer;
street1 : string;
street2 : string;
type
TListSortCompare = function (Item1,
Item2: TContact): Integer;
var
Form1: TForm1;
Contact : PList; //declare record database for contacts
arecord : TContact;
Contacts : TList; //List for the Array of Contacts
function CompareNames(i1, i2: TContact): Integer;
begin
Result := CompareText(i1.lastname1, i2.lastname1) ;
end;
и функцию для вызова сортировки вашего списка
Contacts.Sort(@CompareNames);
Ответ 3
Я хочу поделиться своим решением (на основе ввода, который я собрал здесь).
Это стандартная настройка. Класс filedata, который содержит данные одного файла в общем TObjectList. В списке есть два частных атрибута fCurrentSortedColumn и fCurrentSortAscending для управления порядком сортировки. Метод AsString - это путь и имя файла вместе.
function TFileList.SortByColumn(aColumn: TSortByColums): boolean;
var
Comparison: TComparison<TFileData>;
begin
result := false;
Comparison := nil;
case aColumn of
sbcUnsorted : ;
sbcPathAndName: begin
Comparison := function(const Left, Right: TFileData): integer
begin
Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
end;
end;
sbcSize : begin
Comparison := function(const Left, Right: TFileData): integer
begin
Result := TComparer<int64>.Default.Compare(Left.Size,Right.Size);
if Result = 0 then
Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
end;
end;
sbcDate : begin
Comparison := function(const Left, Right: TFileData): integer
begin
Result := TComparer<TDateTime>.Default.Compare(Left.Date,Right.Date);
if Result = 0 then
Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
end;
end;
sbcState : begin
Comparison := function(const Left, Right: TFileData): integer
begin
Result := TComparer<TFileDataTestResults>.Default.Compare(Left.FileDataResult,Right.FileDataResult);
if Result = 0 then
Result := TComparer<string>.Default.Compare(Left.AsString,Right.AsString);
end;
end;
end;
if assigned(Comparison) then
begin
Sort(TComparer<TFileData>.Construct(Comparison));
// Control the sort order
if fCurrentSortedColumn = aColumn then
fCurrentSortAscending := not fCurrentSortAscending
else begin
fCurrentSortedColumn := aColumn;
fCurrentSortAscending := true;
end;
if not fCurrentSortAscending then
Reverse;
result := true;
end;
end;
Ответ 4
Краткий ответ:
//TComparer --> uses System.Generics.Defaults
myList.Sort(TComparer<TMyRecord>.Construct(function(const Left, Right: TMyRecord): Integer
begin
Result := Left.intVal- Right.intVal;
end));