VafaK
VafaK

Reputation: 534

Sort TStringList by first character after whitespace

I have a TStringList in Delphi. after the items are inserted i call .sort procedure to sort the items. the Items are first names followed by last names. for example: "John Smith". I want to sort the items by last name. I mean by the first character after the space. how can I do this?

and also the items may be unicode strings like persian characters.

Upvotes: 1

Views: 365

Answers (3)

David Heffernan
David Heffernan

Reputation: 613511

I'd use the CustomSort method of TStringList to supply a custom compare function. First of all, let's imagine that we have already got the compare function:

function NameCompareFunc(List: TStringList; Index1, Index2: Integer): Integer;
begin
  Result := ...;
end;

This function will (in due course) return negative to mean less than, positive to mean greater than and zero to mean equal.

Then we sort the list like this:

List.CustomSort(NameCompareFunc);

So, that's the easy bit done. But how do we implement NameCompareFunc? First of all let's split the name into last name and the rest.

procedure SplitName(const Name: string; out Last, Rest: string);
var
  P: Integer;
begin
  P := Pos(' ', Name);
  if P = 0 then begin
    Last := Trim(Name);
    Rest := '';
  end else begin
    Last := Trim(Copy(Name, P+1, MaxInt));
    Rest := Trim(Copy(Name, 1, P-1));
  end;
end;

This is a pretty naive way to split a name. You'd probably want to search for separators starting from the end of the name, but I'll let you decide how to do that.

Now we can implement the compare function:

function NameCompareFunc(List: TStringList; Index1, Index2: Integer): Integer;
var
  Last1, Last2, Rest1, Rest2: string;
begin
  SplitName(List[Index1], Last1, Rest1);
  SplitName(List[Index2], Last2, Rest2);
  Result := AnsiCompareText(Last1, Last2);
  if Result = 0 then begin
    Result := AnsiCompareText(Rest1, Rest2);
  end;
end;

Some notes:

  1. I'm assuming that name comparison should always be case-insensitive.
  2. We use AnsiCompareText to perform locale aware comparison.
  3. If we encounter two names that have the same last name, then we implement a secondary comparison o the rest of the name.

Upvotes: 5

Jens Borrisholt
Jens Borrisholt

Reputation: 6402

You could use the CustomSort methos of Stringlist:

function LastNameCompareStrings(List: TStringList; Index1, Index2: Integer): Integer;
var
  S1, S2: string;
  SpaceIndex: Integer;
begin
  S1 := List[Index1];
  SpaceIndex := Pos(' ', S1);
  if SpaceIndex <> 0 then
    S1 := Copy(S1, 1, SpaceIndex - 1);

  S2 := List[Index2];
  SpaceIndex := Pos(' ', S2);
  if SpaceIndex <> 0 then
    S2 := Copy(S2, 1, SpaceIndex - 1);

  if List.CaseSensitive then
    Result := AnsiCompareStr(S1, S2)
  else
    Result := AnsiCompareText(S1, S2);
end;

procedure TForm7.ButtonFirstNameClick(Sender: TObject);
begin
  NameBuffer.Sort;
  Memo1.Lines.Assign(NameBuffer);
end;

procedure TForm7.ButtonLastNameClick(Sender: TObject);
begin
  NameBuffer.CustomSort(@LastNameCompareStrings);
  Memo1.Lines.Assign(NameBuffer);
end;

I my example I have all your names in a StringList called NameBuffer. Then I've added two buttons to a form where I sort mylist, and display the result on the Screen.

Upvotes: 3

xaid
xaid

Reputation: 750

You could iterate through each item of your StringList (lets call it FullNames), split each string and put the "splits" in two new separate stringlists which you could call FirstNameList and LastNameList.

Now create a third stringlist which you can call ReverseFirstLast, and combine the items from LastNameList with FirstNameList and put them in ReverseNames.

Now you have all names in reverse order. Last name first, and first name last.

You can now sort the ReverseFirstLast-list and do a split&combine method again to reverse orders again and maintain the sorting.

That is one way to do it to get a rough method up and running.

Upvotes: 0

Related Questions