假设我有一个记录数组,我想根据其中一个字段对其进行排序。如何实现最佳效果?
TExample = record
SortOrder : integer;
SomethingElse : string;
end;
var SomeVar : array of TExample;
假设我有一个记录数组,我想根据其中一个字段对其进行排序。如何实现最佳效果?
TExample = record
SortOrder : integer;
SomethingElse : string;
end;
var SomeVar : array of TExample;
您可以向数组的元素添加指针到 TList
,然后使用一个比较函数调用 TList.Sort
,最后创建一个新的数组并按照所需的顺序从 TList 复制值。
但是,如果您正在使用下一个版本 D2009,则有一个新的集合库可用于对数组进行排序。它采用可选的 IComparer<TExample>
实现以进行自定义排序。以下是特定情况下的示例:
TArray.Sort<TExample>(SomeVar , TDelegatedComparer<TExample>.Construct(
function(const Left, Right: TExample): Integer
begin
Result := TComparer<Integer>.Default.Compare(Left.SortOrder, Right.SortOrder);
end));
(我知道这是一年后的事情,但仍然有用。)
Skamradt 建议填充整数值是基于你会使用字符串比较进行排序的假设。这会很慢。对于每个插入调用 format() 更慢。相反,您应该进行整数比较。
您首先需要一个记录类型:
TExample = record
SortOrder : integer;
SomethingElse : string;
end;
您没有说明记录是如何存储的,或者在排序后您希望如何访问它们。因此,让我们假设您将它们放入动态数组中:
var MyDA: Array of TExample;
...
SetLength(MyDA,NewSize); //allocate memory for the dynamic array
for i:=0 to NewSize-1 do begin //fill the array with records
MyDA[i].SortOrder := SomeInteger;
MyDA[i].SomethingElse := SomeString;
end;
现在你想按整数值SortOrder对此数组进行排序。如果您想要一个TStringList(以便可以使用ts.Find方法),则应将每个字符串添加到列表中,并添加SortOrder作为指针。然后根据指针进行排序:
var tsExamples: TStringList; //declare it somewhere (global or local)
...
tsExamples := tStringList.create; //allocate it somewhere (and free it later!)
...
tsExamples.Clear; //now let's use it
tsExamples.sorted := False; //don't want to sort after every add
tsExamples.Capacity := High(MyDA)+1; //don't want to increase size with every add
//an empty dynamic array has High() = -1
for i:=0 to High(MyDA) do begin
tsExamples.AddObject(MyDA[i].SomethingElse,TObject(MyDA[i].SortOrder));
end;
function CompareObjects(ts:tStringList; Item1,Item2: integer): Integer;
begin
Result := CompareValue(Integer(ts.Objects[Item1]), Integer(ts.Objects[Item2]))
end;
tsExamples.CustomSort(@CompareObjects); //Sort the list
现在已经排序,因此您可以从0到.Count-1迭代它,并按排序顺序读取字符串。
<但是假设您不想要一个TStringList,只需要一个按排序顺序排列的数组。或者记录包含更多数据,而您的排序顺序更复杂。您可以跳过添加每个字符串的步骤,只需将数组索引作为TList中的项进行添加即可。除了使用TStringList之外,其他所有内容都与上面相同:
var Mlist: TList; //a list of Pointers
...
for i:=0 to High(MyDA) do
Mlist.add(Pointer(i)); //cast the array index as a Pointer
Mlist.Sort(@CompareRecords); //using the compare function below
function CompareRecords(Item1, Item2: Integer): Integer;
var i,j: integer;
begin
i := integer(item1); //recover the index into MyDA
j := integer(item2); // and use it to access any field
Result := SomeFunctionOf(MyDA[i].SomeField) - SomeFunctionOf(MyDA[j].SomeField);
end;
for i:=0 to Mlist.Count-1 do begin
Something := MyDA[integer(Mlist[i])].SomeField;
end;
TStringList
并通过TString.AddObject(string, Pointer(int_val))
添加记录。TObjectList
,在添加所有记录后调用TObjectList.Sort
并将必要的排序函数作为参数传递。type TComparatorFunction = function(lpItem1: Pointer; lpItem2: Pointer): Integer; cdecl;
procedure qsort(base: Pointer; num: Cardinal; size: Cardinal; lpComparatorFunction: TComparatorFunction) cdecl; external 'msvcrt.dll';
完整示例在这里。
请注意,如果记录很大,则直接对记录数组进行排序可能会很慢。在这种情况下,对指向记录的指针数组进行排序可能会更快(有点类似 List 方法)。
使用数组时,我会使用 quicksort
或者可能是 heapsort
,然后只需更改比较方式为 TExample.SortOrder
,交换部分仍然会作用于数组并交换指针。如果数组非常大,则可能需要链表结构,如果有大量插入和删除。
C语言的例程,可以在这里找到几个
http://www.yendor.com/programming/sort/
另一个站点,但有Pascal源代码
http://www.dcc.uchile.cl/~rbaeza/handbook/sort_a.html
var
someVar: array of TExample;
list: TList<TExample>;
sortedVar: array of TExample;
begin
list := TList<TExample>.Create(someVar);
try
list.Sort;
sortedVar := list.ToArray;
finally
list.Free;
end;
end;
TStringList
有高效的排序方法。
如果你想要排序,请使用一个 TStringList
对象,并将 Sorted
属性设置为 True。
注意:为了提高速度,可以在未排序的 TStringList
中添加对象,最后再将属性更改为 True。
注意:如果要按整数字段排序,请先转换为字符串。
注意:如果存在重复值,则此方法无效。
祝好。
Type
THuman = Class
Public
Name: String;
Age: Byte;
Constructor Create(Name: String; Age: Integer);
End;
Constructor THuman.Create(Name: String; Age: Integer);
Begin
Self.Name:= Name;
Self.Age:= Age;
End;
Procedure Test();
Var
Human: THuman;
Humans: Array Of THuman;
List: TStringList;
Begin
SetLength(Humans, 3);
Humans[0]:= THuman.Create('David', 41);
Humans[1]:= THuman.Create('Brian', 50);
Humans[2]:= THuman.Create('Alex', 20);
List:= TStringList.Create;
List.AddObject(Humans[0].Name, TObject(Humans[0]));
List.AddObject(Humans[1].Name, TObject(Humans[1]));
List.AddObject(Humans[2].Name, TObject(Humans[2]));
List.Sort;
Human:= THuman(List.Objects[0]);
Showmessage('The first person on the list is the human ' + Human.name + '!');
List.Free;
End;