2

I'm having trouble sorting a string list in Delphi XE2. Here's an example:

procedure AddText();
var
  StrList:  TStringList;
begin
  StrList := TStringList.Create();
  StrList.Add('Test1');
  StrList.Sort();
  WriteLn('Sorted: ' + BoolToStr(StrList.Sorted, true)); // Prints "Sorted: false"
  StrList.Add('Test2');
  StrList.Sort();
  WriteLn('Sorted: ' + BoolToStr(StrList.Sorted, true)); // Prints "Sorted: false"
  StrList.Add('Test3');
  StrList.Free();
end;

The problem, as far as I understand, is due to the fact that TStringList.Sorted is never set to true (neither directly nor using SetSorted). Is it just me or is it a bug?

RRUZ
  • 134,889
  • 20
  • 356
  • 483
conciliator
  • 6,078
  • 6
  • 41
  • 66
  • 3
    `Sorted` never tells you about the **state** of the list. `Sorted` is a property that you set that **controls how the list behaves**. When it is false, you add to the list and new items are appended at the end. When `Sorted` is true, new items are always inserted ordered. That is, if your list has 'b', 'c' and 'd', and you then add 'a', it is added to the head of the list. – David Heffernan Sep 05 '12 at 14:04
  • They should have called it `AutoSortNewItems` (; – Jeroen Wiert Pluimers Sep 05 '12 at 16:08

1 Answers1

6

There's nothing in the Classes unit for TStringList.Sort that should make you expect it to change the property. The TStringList.Sort method simply calls CustomSort with the default sorting function. It's not an indicator of the state of the list (sorted or unsorted); it merely determines whether the list is sorted using the internal sort algorithm and new items are added to the proper location instead of the end. From the documentation:

Specifies whether the strings in the list should be automatically sorted.

Set Sorted to true to cause the strings in the list to be automatically sorted in ascending order. Set Sorted to false to allow strings to remain where they are inserted. When Sorted is false, the strings in the list can be put in ascending order at any time by calling the Sort method.

When Sorted is true, do not use Insert to add strings to the list. Instead, use Add, which will insert the new strings in the appropriate position. When Sorted is false, use Insert to add strings to an arbitrary position in the list, or Add to add strings to the end of the list

You're using it wrong in the first place, though. Simply add all of your strings to the StringList, and then set Sorted := True;. It will properly set the property value and call the internal Sort method for you automatically.

procedure AddText();
var
  StrList:  TStringList;
begin
  StrList := TStringList.Create();
  StrList.Add('Test1');
  StrList.Add('Test2');
  StrList.Add('Test3');
  StrList.Sorted := True;
  // Do whatever
  StrList.Free;
end;

(You particularly don't want to call Sort() after every single item is added; that's extremely slow and inefficient.)

Community
  • 1
  • 1
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • +1. another option is to set `.Sorted := True` *before* adding the strings. when you add a new string it will be inserted in the right order position. I believe this is faster (since `Add` method will use `Find` and add the new string right after that index). – kobik Sep 05 '12 at 13:44
  • @kobik, it's actually slower (because it has to use `Find` and do an insert after every string. If you set `Sorted := False`, add all the strings, and then set `Sorted` to true, the sort is only done once and there is no find or insert. – Ken White Sep 05 '12 at 14:07
  • 1
    That's still O(n log n) in comparisons. Same as Quicksort. Unless the list is already sorted! But you pay the penalty of the insertions having to move up the existing values. – David Heffernan Sep 05 '12 at 14:24
  • Ken is probably right with the speed issue - Inserts vs ExchangeItems of the sort procedure. – kobik Sep 05 '12 at 14:26
  • @David: O(n log n) is great, but it doesn't beat doing no comparisons at all. :-) And I mentioned the insertion overhead in my comment as well. – Ken White Sep 05 '12 at 14:32
  • Actually I meant that when the list is already sorted, quick sort is O(n^2)! – David Heffernan Sep 05 '12 at 14:34
  • @Ken - once i opposed to PChar to string redundant conversion, and was told about premature optimizations ;) – Arioch 'The Sep 05 '12 at 14:47
  • @Arioch'The: You'll notice that the comment about speed was a **minor footnote** to the answer. I was simply mentioning it, not discussing "optimization". With that being said, it doesn't take a large number of strings in a stringlist before the inefficiency of sorting each time one is added to be noticed. :-) – Ken White Sep 05 '12 at 14:55
  • Indeed. Calling `Sort` after every addition makes list population O(n^3)! – David Heffernan Sep 05 '12 at 15:45
  • @David, QuickSort isn't O(n^2) when the list is already sorted. Actually, an already sorted list is Quicksort's very best case(O(n log n)) when the QuickSort's implementation select it's Pivot at the middle of the list. Quicksort is O(n^2) only when the Pivot is moved to the first or last element of the (sub)list on each and every iteration. – Ken Bourassa Sep 05 '12 at 16:50
  • @kenb do you know the pivot strategy in delphi's string list sort? – David Heffernan Sep 05 '12 at 17:14
  • @David, see it yourself. It's in `Classes.pas`, ~ line 6325 in XE2 (`TStringList.QuickSort`). The `SCompare` it uses as the compare function is at ~ line 6378. – Ken White Sep 05 '12 at 17:28
  • Thanks Ken, my assumptions about TStringList.Sorted was clearly faulty. – conciliator Sep 06 '12 at 08:12