简体   繁体   中英

Custom Sort a Delphi TStringList name/value pairs as integers

I have a TStringList of Name/Value pairs. The names are all integer values 9stored as strings of course) and the values are all strings (comma separated).

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.

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 )

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.

  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.

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. 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.

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 ! Instead, they perform a binary search for the correct insertion location and then insert there. Instead of overriding Sort , you could try overriding CompareStrings :

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. It might also break Find , but you might want that, depending on how you want to treat elements with the same numeric key. ( 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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