Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
494 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi



Loremo - the 1.5 liter car coming in 2009




Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (0)


TString Super Sort Class (descending,ignore case and others)Format this article printer-friendly!Bookmark function is only available for registered users!
TSuperSort
Product:
Delphi 5.x (or higher)
Category:
Algorithm
Skill Level:
Scoring:
Last Update:
11/01/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet TSTRINGS SORT DESCENDING
Times Scored:
5
Visits:
5473
Uploader: Mike Heydon
Company: EOH
Reference: mheydon@pgbison.co.za
 
Question/Problem/Abstract:
TStringList has a Sort method and a Sorted property. This feature is not available in it's useful descendant TStrings. This class allows sorting of TString objects with extra functionality ala UNIX style parameters. (Yes I know UNIX is a four letter word but they do have some neat features). The SORT algorythm utilizes the QUICK SORT method.

The features I have implemented are

  Options
  SORT DESCENDING                  - srtDescending
  TREAT SORT FIELD AS NUMERIC      - srtEvalNumeric
  IGNORE LEADING BLANKS IN FIELD   - srtIgnoreBlank
  IGNORE CASE OF FIELD             - srtIgnoreCase

  Switches
  -k Start,End position of substring for search
  -f Field number of a delimited string (Zero column based)
  -d Character delimiter for -f switch (Default = SPACE)

In it's simplest form it just sorts the TStrings ascending
eg.  SuperSort.SortStrings(Memo1.Lines,[]);

Assume a semi-colon delimited list like ..
  'Mike;34;Green'
  'harry;25;Red'
  'Jackie;6;Black'
  'Bazil;9,Pink'
  'john;52;Blue'

To sort this list DESCENDING on AGE (Field 1) and ignore case
     SuperSort(MyStrings,
              ['-f 1','-d ;'],
              [srtDescending,srtEvalNumeric,srtIgnoreCase]);

Assume a string list of ...
   '1999 12 20 AA432 Comment 1'
   '2002 10 12 SWA12 Some other words'
   '1998 09 11 BDS65 And so on and so on'

To sort this list on ITEM CODE (Positions 12 to 17) with no options
     SuperSort(MyStrings,['-k 12,17']);


Methods :

procedure SortStrings(StringList : TStrings;
                      Switches : array of string;
                      Options : TSuperSortOptionSet = []);

   Switches is a string array of -k,-d and -f settings. If it is set to
   empty array [] then NO switches are active.

   Options is an OPTIONAL set of [srtDescending,srtIgnoreCase,
                                  srtIgnoreBlank,srtEvalNumeric]
   The default is empty set []

Properties :

SortTime : TDateTime;

   Returns the time taken for the sort for stats purposes.

Usage Example :

  uses SuperSort;

  procedure TForm1.Test;
  var Srt : TSuperSort
  begin
    Srt := TSuperSort.Create;
    Srt.SortStrings(Memo1.Lines,[],[srtIgnoreBlank]);
    Label1.Caption := 'Time : ' +
                              FormatDateTine('hh:nn:ss:zzz',Srt.SortTime);
    Srt.Free;
  end;
Answer:



unit SuperSort;
interface
uses Classes,SysUtils;

// ==========================================================================
// Class TSuperSort
// Mike Heydon Nov 2002
//
// Sort class that implements Unix style sorts including ..
//
// SWITCHES
// --------
// -k [StartPos,EndPos]  - Keyfield to sort on. Start and End pos in string
// -d [Field Delimiter]  - Delimter to use with -f switch. default = SPACE
// -f [FieldNumber]      - Zero based field number delimeted by -d
//
// OPTIONS SET
// ============
// srtDescending         - Sort descending
// srtIgnoreCase         - Ignore case when sorting
// srtIgnoreBlank        - Ignore leading blanks
// srtEvalNumeric        - Treat sort items as NUMERIC
//
// ==========================================================================


type
      // Sort Options
      TSuperSortOptions = (srtDescending,srtIgnoreCase,
                           srtIgnoreBlank,srtEvalNumeric);
      TSuperSortOptionSet = set of TSuperSortOptions;

     // ============
     // TSuperSort
     // ============
     TSuperSort = class(TObject)
     protected
       function GetKeyString(const Line : string) : string;
       procedure QuickSortStrA(SL : TStrings);
       procedure QuickSortStrD(SL : TStrings);
       procedure ResolveSwitches(Switches : array of string);
     private
       FSortTime : TDateTime;
       FIsSwitches,
       FIsPositional,
       FIsDelimited,
       FDescending,
       FIgnoreCase,
       FIgnoreBlank,
       FEvalDateTime,
       FEvalNumeric : boolean;
       FFieldNum,
       FStartPos,FEndPos : integer;
       FDelimiter : char;
     public
       procedure SortStrings(StringList : TStrings;
                             Switches : array of string;
                             Options : TSuperSortOptionSet = []);
       property SortTime : TDateTime read FSortTime;
     end;

// --------------------------------------------------------------------------
implementation

const BLANK    = -1;
      EMPTYSTR = '';

// ================================================
// INTERNAL CALL
// Resolve switches and set internal variables
// ================================================

procedure TSuperSort.ResolveSwitches(Switches : array of string);
var i : integer;
    Sw,Data : string;
begin
  FStartPos := BLANK;
  FEndPos := BLANK;
  FFieldNum := BLANK;
  FDelimiter := ' ';
  FIsPositional := false;
  FIsDelimited := false;

  for i := Low(Switches) to High(Switches) do begin
    Sw := trim(Switches[i]);
    Data := trim(copy(Sw,3,1024));
    Sw := UpperCase(copy(Sw,1,2));

    // Delimiter
    if Sw = '-D' then begin
      if length(Data) > 0 then FDelimiter := Data[1];
    end;

    // Field Number
    if Sw = '-F' then begin
      FIsSwitches := true;
      FIsDelimited := true;
      FFieldNum := StrToIntDef(Data,BLANK);
      Assert(FFieldNum <> BLANK,'Invalid -f Switch');
    end;

    // Positional Key
    if Sw = '-K' then begin
      FIsSwitches := true;
      FIsPositional := true;
      FStartPos := StrToIntDef(trim(copy(Data,1,pos(',',Data) - 1)),BLANK);
      FEndPos := StrToIntDef(trim(copy(Data,pos(',',Data) + 1,1024)),BLANK);
      Assert((FStartPos <> BLANK) and (FEndPos <> Blank),'Invalid -k Switch');
    end;

  end;
end;


// ====================================================
// INTERNAL CALL
// Resolve the Sort Key part of the string based on
// the Switches parameters
// ====================================================

function TSuperSort.GetKeyString(const Line : string) : string;
var Key : string;
    Numvar : double;
    DCount,i,DPos : integer;
    Tmp : string;
begin
  // Default
  Key := Line;
  // Extract Key from switches -k takes precedence
  if FIsPositional then
    Key := copy(Key,FStartPos,FEndPos)
  else
    if FIsDelimited then begin
      DPos := 0;
      DCount := 0;
      for i := 1 to length(Key) do begin
        if Key[i] = FDelimiter then inc(DCount);
        if DCount = FFieldNum then begin
           if FFieldNum = 0 then
             DPos := 1
           else
             DPos := i + 1;
          break;
        end;
      end;

      if DCount < FFieldNum then
        // No such Field Number
        Key := EMPTYSTR
      else begin
        Tmp := copy(Key,DPos,4096);
        DPos := pos(FDelimiter,Tmp);
        if DPos = 0 then
          Key := Tmp
        else
          Key := copy(Tmp,1,DPos - 1);
       end;
    end;

  // Resolve Options
  if FEvalNumeric then begin
    Key := trim(Key);
    // Strip any commas
    for i := length(Key) downto 1 do
      if Key[i] = ',' then delete(Key,i,1);
    try
      Numvar := StrToFloat(Key);
    except
      Numvar := 0.0;
    end;
    Key := FormatFloat('############0.000000',Numvar);
    // Leftpad num string
    Key := StringOfChar('0',20 - length(Key)) + Key;
  end;

  // Ignores N/A for Numeric and DateTime
  if not FEvalNumeric and not FEvalDateTime then begin
    if FIgnoreBlank then Key := trim(Key);
    if FIgnoreCase then Key := UpperCase(Key);
  end;

  Result := Key;
end;

// ==============================================
// INTERNAL CALL
// Recursive STRING quick sort routine ASCENDING.
// ==============================================

procedure TSuperSort.QuickSortStrA(SL : TStrings);

  procedure Sort(l,r : integer);
  var i,j : integer;
      x,Tmp : string;
  begin
    i := l;
    j := r;
    x := GetKeyString(SL[(l + r) div 2]);

    repeat
      while GetKeyString(SL[i]) < x do inc(i);
      while x  < GetKeyString(SL[j]) do dec(j);
      if i <= j then  begin
        Tmp := SL[j];
        SL[j] := SL[i];
        SL[i] := Tmp;
        inc(i);
        dec(j);
      end;
    until i > j;

    if l < j then Sort(l,j);
    if i < r then Sort(i,r);
  end;

begin
  if SL.Count > 0 then begin
    SL.BeginUpdate;
    Sort(0,SL.Count - 1);
    SL.EndUpdate;
  end;
end;

// ==============================================
// INTERNAL CALL
// Recursive STRING quick sort routine DECENDING
// ==============================================

procedure TSuperSort.QuickSortStrD(SL : TStrings);
  procedure Sort(l,r : integer);
  var i,j : integer;
      x,Tmp : string;
  begin
    i := l;
    j := r;
    x := GetKeyString(SL[(l + r) div 2]);

    repeat
      while GetKeyString(SL[i]) > x do inc(i);
      while x  > GetKeyString(SL[j]) do dec(j);
      if i <= j then  begin
        Tmp := SL[j];
        SL[j] := SL[i];
        SL[i] := Tmp;
        inc(i);
        dec(j);
      end;
    until i > j;

    if l < j then Sort(l,j);
    if i < r then Sort(i,r);
  end;

begin
  if SL.Count > 0 then begin
    SL.BeginUpdate;
    Sort(0,SL.Count - 1);
    SL.EndUpdate;
  end;
end;



// ====================
// Sort a stringlist
// ====================

procedure TSuperSort.SortStrings(StringList : TStrings;
                                 Switches : array of string;
                                 Options : TSuperSortOptionSet = []);
var StartTime : TDateTime;
begin
  StartTime := Now;
  FDescending := (srtDescending in Options);
  FIgnoreCase := (srtIgnoreCase in Options);
  FIgnoreBlank := (srtIgnoreBlank in Options);
  FEvalNumeric := (srtEvalNumeric in Options);
  ResolveSwitches(Switches);

  if FDescending then
    QuickSortStrD(StringList)
  else
    QuickSortStrA(StringList);

  FSortTime := Now - StartTime;
end;


end.





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment













 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
M. Maes
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)