I have a stringlist (actually, a CheckListBox.Items) that has it's contents sorted by
the String list value.
Now my users want this same list of items to have a different sort order -- I can load
an integer value into the Items.Objects that would contain the correct sort order, but
I'm not sure how to have the Items re-sort to ascending Object order. I guess I'm
looking for the quick sort that TList has, but don't know if/how this sort can be called
from a stringlist?
Thanks for any suggestions! Brian
A TstringList has two main advantages over a Tlist. One, it's a Tstrings
descendant. Two, its services are oriented towards strings.
The first advantage tends to indicate that you should stick to a
TstringList. Your problem is that, where a Tlist gives you a parameterized
sort, a TstringList gives you one sort (alpabetical on strings), and that's
it. TstringList.sort is virtual, but there is no easy way to get a quickSort
if you re-implement it. Its quickSort is private. Also, if you override
Sort, you can forget about Sorted, or go to more than twice the trouble.
In some other cases, the solution is simply to plug in the integer at the
beginning of each string. Not here, because this is a CheckListBox.Items.
There are various solutions, depending on conditions. For instance, you
could shoot the list contents over to a Tlist, sort there, then reload. But,
from what you say, I gather that the list isn't all that long. You might
override Sort and use any easy sorting code you want. Or you might override
AddObject so that all additions are done in order.
What do you think?
PhR
The list can number into the thousands or tens of thousands, and to make
the task more complicated, the users may want either an Unsorted list,
String Sorted List, or Integer Sorted List.
From your answer I get the impression that overriding sort will give me more
complications than sending the values to a TList for the integer sort.
Since that method would require moving the values to the list and back, I
guess I will also try putting the sort value at the beginning of the string,
sort, then strip it out (I have no idea which technique would be faster).
Again, thanks for your ideas. Brian
Sometimes I wonder if we are use to that working with Delphi? <g>
Ed
An alternative would be to go steal the quickSort source from the
TstringList source, and modify it for integer(objects[n]).
If you go the string-prefix route, don't think of stuffing the raw bytes in
there, TstringList.sort misbehaves on chars like #0. Use IntToHex.
PhR
ie:-
procedure TForm1.SortButton1Click(Sender: TObject);
var ts:TStringlist;
i:Integer;
s:String;
cc:Char;
begin
ts:=TStringList.create;
try
ts.sorted:=False;
{ Build a string based on the object and string value and checked state}
for i:=0 to Checklistbox1.Items.Count-1 do
begin
if Checklistbox1.Checked[i] then
cc:='!'
else
cc:=' ';
s:=Format('%8.8d',[integer(Checklistbox1.Items.Objects[i])])+cc+
Checklistbox1.Items[i];
ts.Addobject(s,Checklistbox1.Items.Objects[i]);
end;
ts.sorted:=True;
{Now put them back and decode the string we've built}
Checklistbox1.Items.beginupdate;
Checklistbox1.Items.Clear;
for i:=0 to ts.Count-1 do
begin
s:=ts[i];
system.Delete(s,1,8);
Checklistbox1.Items.addobject(Copy(s,2,Length(s)-1),ts.Objects[i]);
if s[1]='!' then
Checklistbox1.Checked[Checklistbox1.Items.Count-1]:=True;
end;
Checklistbox1.Items.endupdate;
finally
ts.Free;
end;
end;
It should work :)
Ian
Philippe Ranger <.> wrote in message news:7gae4l$ol...@forums.borland.com...
I imagine using the modified quickSort will be the fastest way to get the result.
Also, I wasn't aware that IntToHex would allow you to specify the number
of digits -- I was expecting to have to do IntToStr and left pad the
string with zeroes.
-Brian
I'm going to give the procedure a try. I guess the only possible obstacle to it working
would be that the CheckListBox uses TStrings directly instead of TStringList
(there isn't any Sort method; it seems to pass the Sort to some Windows function?), but
if that proves to be a complication I can always do the sort in a TStringList and then
Assign it to the CheckListBox.
--Brian
Brian,
What you're trying to do is definitely the easiest way. To assign an
integer 'tag' to the object property simply do
objects[i]:=pointer(WhateverInt) and to compare just cast the object
back to an integer.
Then use a simple quicksort with the compare being a compare of the
integers. (This, by the way will be a much faster sort than one
comparing strings).
Here's an example of what your sort will look like:
Procedure BriansQuickSortByNumericTag(var list: TStringlist);
procedure TaggedQuickSort(l, r: integer);
function getTag(const i: integer): integer;
begin
result:= integer(list.objects[i]);
end;
var
i, j, tag: integer;
begin
repeat
i := l; j := r; tag:= getTag((i+j) shr 1);
repeat
while getTag(i) - tag < 0 do inc(i);
while getTag(j) - tag > 0 do dec(j);
if i <= j then begin list.exchange(i, j); inc(i); dec(j); end;
until i > j;
if l < j then TaggedQuickSort(l, j);
l := i;
until i >= r;
end;
begin
if not list.sorted and (list.count>1) then
TagQuickSort(0,list.count-1);
end;
Hope that helps,
Chad
One more thought. I use a StringGrid to display this type of info with a
procedure for sorting the StringGrid on a selected column (using any of the
suggested sort methods you choose). I've made this 'sort of' genric so I can
sort any Stringgrid on any column.
bob mackey
In any case, thanks for the suggestion! Brian