简体   繁体   English

自定义将Delphi TStringList名称/值对排序为整数

[英]Custom Sort a Delphi TStringList name/value pairs as integers

I have a TStringList of Name/Value pairs. 我有一个名称/值对的TStringList。 The names are all integer values 9stored as strings of course) and the values are all strings (comma separated). 这些名称都是9个整数值,当然是字符串),值都是字符串(以逗号分隔)。

eg 例如

5016=Catch the Fish!,honeyman,0
30686=Ozarktree1 Goes to town,ozarktreel,0

. . .

I would like to call the add routine and add new lines in the TStringlist, but need a way to sort the list afterwards. 我想调用add例程并在TStringlist中添加新行,但之后需要一种方法对列表进行排序。

eg 例如

Tags.Add(frmTag.edtTagNo.Text + '=' +
         frmTag.edtTitle.Text + ',' +
         frmTag.edtCreator.Text + ',' +
         IntToStr(ord(frmTag.cbxOwned.Checked)));
Tags.Sort;

Here is what I tried: 这是我尝试过的:

Tags:= TStringList.Create;
 Tags.CustomSort(StringListSortComparefn);
 Tags.Sorted:= True;

my custom sort routine: 我的自定义排序例程:

function StringListSortComparefn(List: TStringList; Index1, Index2: Integer): Integer;
var
 i1, i2 : Integer;
begin
 i1 := StrToIntDef(List.Names[Index1], 0);
 i2 := StrToIntDef(List.Names[Index2], 0);
 Result:= CompareValue(i1, i2);
end;

However, it still seems to be sorting them like strings instead of integers. 但是,它似乎仍然像字符串而不是整数一样对它们进行排序。

I even tried creating my own class: 我甚至尝试创建自己的类:

type
 TXStringList = class(TStringList)
 procedure Sort;override;
end;

implementation

function StringListSortComparefn(List: TStringList; Index1, Index2: Integer): Integer;
var
i1, i2 : Integer;
begin
i1 := StrToIntDef(List.Names[Index1], 0);
i2 := StrToIntDef(List.Names[Index2], 0);
Result:= CompareValue(i1, i2);
end;

procedure TXStringList.Sort;
begin
 CustomSort(StringListSortComparefn);
end;

I even tried some examples on SO (eg Sorting TSTringList Names property as integers instead of strings ) 我甚至在SO上尝试了一些例子(例如,将TSTringList Names属性排序为整数而不是字符串

Can someone tell me what I am doing wrong? 有人能告诉我我做错了什么吗? Everytime, the list gets sorted as strings and not as integers. 每次,列表都按字符串排序,而不是整数。

30686=Ozarktree1 Goes to town,ozarktreel,0
5016=Catch the Fish!,honeyman,0

You can do a simple integer subtraction: 你可以做一个简单的整数减法:

function StringListSortComparefn(List: TStringList; Index1, Index2: Integer): Integer;
var
 i1, i2 : Integer;
begin
 i1 := StrToIntDef(List.Names[Index1], 0);
 i2 := StrToIntDef(List.Names[Index2], 0);
 Result := i1 - i2
end;

To reverse the sort order, simply reverse the operands in the subtraction: 要反转排序顺序,只需在减法中反转操作数:

Result := i2 - i1;

Here's a quick, compiilable console example: 这是一个快速,可编译的控制台示例:

program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes;

function StringListSortProc(List: TStringList; Index1, Index2: Integer): Integer;
var
  i1, i2: Integer;
begin
  i1 := StrToIntDef(List.Names[Index1], -1);
  i2 := StrToIntDef(List.Names[Index2], -1);
  Result := i1 - i2;
end;

var
  SL: TStringList;
  s: string;
begin
  SL := TStringList.Create;
  SL.Add('3456=Line 1');
  SL.Add('345=Line 2');
  SL.Add('123=Line 3');
  SL.Add('59231=Line 4');
  SL.Add('545=Line 5');
  WriteLn('Before sort');
  for s in SL do
    WriteLn(#32#32 + s);
  SL.CustomSort(StringListSortProc);
  WriteLn('');
  WriteLn('After sort');
  for s in SL do
    WriteLn(#32#32 + s);
  ReadLn;
  SL.Free;
end.

And the resulting output: 结果输出:

Before sort
  3456=Line 1
  345=Line 2
  123=Line 3
  59231=Line 4
  545=Line 5

After sort
  123=Line 3
  345=Line 2
  545=Line 5
  3456=Line 1
  59231=Line 4

The question is, do you require the list to remain sorted? 问题是,您是否要求列表保持排序? Or is it sufficient to sort it at the end, after all the items have been added. 或者在添加完所有项目后,最后对它进行排序就足够了。

If you just need to be able to sort the list as needed, you're first example is almost correct. 如果您只需要能够根据需要对列表进行排序,那么您的第一个示例几乎是正确的。 You just need to call CustomSort at the end, after your items have been added. 添加完项目后,您最后需要在最后调用CustomSort。

  Tags := tStringList . Create;
  Tags . Add ( '5016=Catch the Fish!,honeyman,0' );
  Tags . Add ( '30686=Ozarktree1 Goes to town,ozarktreel,0' );
  Tags.CustomSort(StringListSortComparefn);

If you need the list to stay sorted, then you need to override CompareStrings. 如果您需要列表保持排序,则需要覆盖CompareStrings。

type
 TXStringList = class(TStringList)
    function CompareStrings(const S1, S2: string): Integer; override;
end;

function NumberOfNameValue ( const S : string ) : integer;
begin
  Result := StrToIntDef(copy(S,1,pos('=',S)-1), 0);
end;

function txStringList . CompareStrings ( const S1, S2 : string ) : integer;
var
 i1, i2 : Integer;
begin
 i1 := NumberOfNameValue ( S1 );
 i2 := NumberOfNameValue ( S2 );
 Result:= CompareValue(i1, i2);
end;


begin
  Tags := txstringlist . Create;
  Tags . Sorted := true;
  Tags . Add ( '5016=Catch the Fish!,honeyman,0' );
  Tags . Add ( '30686=Ozarktree1 Goes to town,ozarktreel,0' );
 // List will be correctly sorted at this point. 
end;

The CustomSort command is a one-time operation. CustomSort命令是一次性操作。 You appear to be using it as though you're setting a property so that further sorting will use the custom comparison function, but that's not really how it works. 您似乎正在使用它,就好像您正在设置属性,以便进一步排序将使用自定义比较功能,但这不是它的工作原理。 It sorts the (newly created, empty) list once. 它对(新创建的,空的)列表进行一次排序。 Then, when you set the Sorted property, you re-sort the list using the default comparison, and you specify that any further additions to the list should be inserted using that default sort order. 然后,当您设置Sorted属性时,使用默认比较重新排序列表, 指定应使用该默认排序顺序插入列表的任何进一步添加。

When you override the Sort method, you're a little closer to a solution, but insertions to a sorted list (where Sorted=True ) do not actually call Sort ! 当您重写Sort方法时,您更接近解决方案,但插入到排序列表( Sorted=True )实际上并不调用Sort Instead, they perform a binary search for the correct insertion location and then insert there. 相反,他们执行二进制搜索正确的插入位置,然后插入。 Instead of overriding Sort , you could try overriding CompareStrings : 您可以尝试重写CompareStrings ,而不是重写Sort

type
  TXStringList = class(TStringList)
  protected
    function CompareStrings(const S1, S2: string): Integer; override;
  end;

function TXStringList.CompareStrings(const S1, S2: string): Integer;
var
  i1, i2, e1, e2: Integer;
begin
  Val(S1, i1, e1);
  Assert((e1 = 0) or (S1[e1] = NameValueSeparator));
  Val(S2, i2, e2);
  Assert((e2 = 0) or (S2[e2] = NameValueSeparator));
  Result := CompareValue(i1, i2);
end;

Beware that this will break the IndexOf method, though. 但请注意,这会破坏IndexOf方法。 It might also break Find , but you might want that, depending on how you want to treat elements with the same numeric key. 它也可能会破坏Find ,但您可能需要这样,具体取决于您希望如何使用相同的数字键处理元素。 ( Find is what's used to locate the correct insertion point of a sorted list, and with the above code, it would treat all elements with the same key as equal.) They all use CompareStrings just like Sort does. Find是用于定位排序列表的正确插入点的内容,并且使用上面的代码,它将使用相同的键处理所有元素。)它们都像Sort一样使用CompareStrings

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM