假设我有一系列记录,我想根据记录中的一个字段进行排序。实现这一目标的最佳方法是什么?
TExample = record
SortOrder : integer;
SomethingElse : string;
end;
var SomeVar : array of TExample;
答案 0 :(得分:36)
您可以将指向数组元素的指针添加到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));
答案 1 :(得分:12)
(我知道这是一年之后,但仍然是有用的东西。)
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;
注意将Integer SortOrder转换为TObject指针的技巧,该指针存储在TStringList.Object属性中。 (这取决于Integer和Pointer的大小相同。)某处我们必须定义一个函数来比较TObject指针:
function CompareObjects(ts:tStringList; Item1,Item2: integer): Integer;
var i,j: integer;
begin
Result := integer(ts.Objects[i]) - integer(ts.Objects[j];
end;
现在,我们可以通过调用.CustomSort而不是.Sort(对字符串值进行排序)来对.Object上的tsList进行排序。
tsExample.CustomSort(@CompareObjects); //Sort the list
现在对TStringList进行了排序,因此您可以将其从0迭代到.Count-1并按排序顺序读取字符串。
但是假设您不想要TStringList,只需要按排序顺序排列数组。或者记录包含的数据多于此示例中的一个字符串,并且排序顺序更复杂。您可以跳过添加每个字符串的步骤,只需将数组索引添加为TList中的Items。除了使用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;
现在Mlist已经排序,使用它作为查找表来按排序顺序访问数组:
for i:=0 to Mlist.Count-1 do begin
Something := MyDA[integer(Mlist[i])].SomeField;
end;
当我遍历TList时,我们按排序顺序返回数组索引。我们只需要将它们转回整数,因为TList认为它们是指针。
我喜欢这样做,但你也可以通过添加数组元素的地址而不是它的索引来在TList中添加实数指向数组元素的指针。然后使用它们,您可以将它们作为指向TExample记录的指针。这就是Barry Kelly和CoolMagic在答案中所说的。
答案 2 :(得分:3)
如果您需要按字符串排序,请使用已排序的TStringList
和
按TString.AddObject(string, Pointer(int_val))
添加记录。
但是如果需要按整数字段和字符串排序 - 请使用TObjectList
并在添加所有记录后调用TObjectList.Sort
并将必要的排序函数作为参数。
答案 3 :(得分:2)
这完全取决于您要排序的记录数。如果你只排序少于几百,那么其他排序方法工作正常,如果你要排序更多,那么好好看看旧的可靠的Turbo Power SysTools项目。源中包含一个非常好的排序算法。能够以有效的方式对数百万条记录进行排序的工作非常出色。
如果要使用tStringList方法对记录列表进行排序,请确保在将整数插入列表之前将其填充到右侧。例如,您可以使用格式('%。10d',[rec.sortorder])右对齐10位数。
答案 4 :(得分:1)
使用数组,我会使用quicksort
或可能heapsort
,只需将比较更改为使用TExample.SortOrder
,交换部分仍然只会对数组执行操作和交换指针。如果数组非常大,那么如果存在大量插入和删除,您可能需要链表结构。
基于C的例程,这里有几个 http://www.yendor.com/programming/sort/
另一个网站,但有pascal源 http://www.dcc.uchile.cl/~rbaeza/handbook/sort_a.html
答案 5 :(得分:1)
快速排序算法通常在需要快速排序时使用。例如,Delphi(也就是)将它用于List.Sort。 Delphi List可用于对任何东西进行排序,但它是一个重量级容器,应该看起来像结构上的指针数组。它是重量级的,即使我们在这个线程中使用像Guy Gordon这样的技巧(放置索引或代替指针的任何东西,或者如果它们小于32位则直接放置值):我们需要构建一个列表等等...... / p>
因此,轻松快速地对struct数组进行排序的替代方法可能是使用msvcrt.dll中的qsort C runtime function。
这是一个可能很好的声明(警告:仅在Windows上可移植的代码)。
type TComparatorFunction = function(lpItem1: Pointer; lpItem2: Pointer): Integer; cdecl;
procedure qsort(base: Pointer; num: Cardinal; size: Cardinal; lpComparatorFunction: TComparatorFunction) cdecl; external 'msvcrt.dll';
完整示例here。
请注意,如果记录很大,直接对记录数组进行排序可能会很慢。在这种情况下,对指向记录的指针数组进行排序可能会更快(以某种方式像List方法)。
答案 6 :(得分:0)
使用Wikipedia提出的排序算法之一。 Swap函数应使用与数组元素相同类型的临时变量交换数组元素。如果您希望具有相同SortOrder整数值的条目保持其首先的顺序,请使用稳定的排序。
答案 7 :(得分:0)
TStringList
具有高效的排序方法
如果要进行排序,请将TStringList
对象的Sorted
属性设置为True。
注意:要获得更快的速度,请在未排序的TStringList
中添加对象,最后将属性更改为True。
注意:对于按整数字段排序,请转换为字符串
注意:如果存在重复值,则此方法不是有效的。
问候。
答案 8 :(得分:0)
如果你有Delphi XE2或更新版本,你可以尝试:
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;
答案 9 :(得分:-1)
我创建了一个非常简单的例子,如果sort字段是一个字符串,它就能正常工作。
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;