Board index » delphi » Stringlist sorting

Stringlist sorting

If I use the Tstringlist.compare function, is there a way to sort a list
using a key within each string?

i.e.  if
s[0]='AB0'
s[1]='CB1'
s[2]='HB2'
s[3]='BB4'
s[4]='ZB3'

Is there a way to sort the list using the third character?

Thanks,
Chris

 

Re:Stringlist sorting


Hi !

You may use TStringList.CustomSort:

function Compare2ndLetter(List: TStringList; Index1, Index2: Integer):
Integer;
var
  Tmp1,
  Tmp2: string;
begin
  Tmp1:=List[Index1];
  Tmp2:=List[Index2];
  if (Length(Tmp1) >= 2) and (Length(Tmp2) >= 2) then
    result:=ASCIICompareText(Tmp1[2], Tmp2[2])
  else
    result:=0; // Then what ???
end;

begin
  StringList.CustomSort(Compare2ndLetter);
end;

--
Regards,

Bj?rge S?ther
bjorge@haha_itte.no
-------------------------------------
I'll not spend any money on American Software products
until armed forces are out of Iraq.
"Chris Lock" <chris.l...@kentdesigns.co.uk> skrev i melding
news:cTFya.65199$UJ5.2680@news-lhr.blueyonder.co.uk...

Quote
> If I use the Tstringlist.compare function, is there a way to sort a list
> using a key within each string?

> i.e.  if
> s[0]='AB0'
> s[1]='CB1'
> s[2]='HB2'
> s[3]='BB4'
> s[4]='ZB3'

> Is there a way to sort the list using the third character?

> Thanks,
> Chris

Re:Stringlist sorting


Quote
"Chris Lock" <chris.l...@kentdesigns.co.uk> wrote in message

news:cTFya.65199$UJ5.2680@news-lhr.blueyonder.co.uk...

Quote
> If I use the Tstringlist.compare function, is there a way to sort a list
> using a key within each string?

> i.e.  if
> s[0]='AB0'
> s[1]='CB1'
> s[2]='HB2'
> s[3]='BB4'
> s[4]='ZB3'

> Is there a way to sort the list using the third character?

Well Compare callback goes like this, right

function Compare(Item1, Item2 : pointer) : integer;

So if you are sure you'll have always a three characters yo can do this:

var p1, p2 : pchar;
p1 := pchar(Item1) + 2; // setting p1 to point to the third character
p2 := pchar(Item2) + 2;
Result := StrIComp(p1, p2); // not sure about the syntax of StrIComp

(not-tested, HTH)

Re:Stringlist sorting


Thanks all - but its a bit more complicated than that!

 Ive got a series of strings, each containing several fields separated
by EndOfField and I'm displaying them in a stringgrid (Topgrid). I need
to sort by column - date, integer, float etc.
I was hoping there was an easy way to sort the underlying stringlist.
Thanks for your ideas - I'll battle on.
Chris

Quote
"Bj?rge S?ther" <bjorge@hahaha_itte.no> wrote in message

news:DSGya.229$Hb.5066@news4.e.nsc.no...
Quote
> Hi !

> You may use TStringList.CustomSort:

> function Compare2ndLetter(List: TStringList; Index1, Index2: Integer):
> Integer;
> var
>   Tmp1,
>   Tmp2: string;
> begin
>   Tmp1:=List[Index1];
>   Tmp2:=List[Index2];
>   if (Length(Tmp1) >= 2) and (Length(Tmp2) >= 2) then
>     result:=ASCIICompareText(Tmp1[2], Tmp2[2])
>   else
>     result:=0; // Then what ???
> end;

> begin
>   StringList.CustomSort(Compare2ndLetter);
> end;

> --
> Regards,

> Bj?rge S?ther
> bjorge@haha_itte.no
> -------------------------------------
> I'll not spend any money on American Software products
> until armed forces are out of Iraq.
> "Chris Lock" <chris.l...@kentdesigns.co.uk> skrev i melding
> news:cTFya.65199$UJ5.2680@news-lhr.blueyonder.co.uk...
> > If I use the Tstringlist.compare function, is there a way to sort a
list
> > using a key within each string?

> > i.e.  if
> > s[0]='AB0'
> > s[1]='CB1'
> > s[2]='HB2'
> > s[3]='BB4'
> > s[4]='ZB3'

> > Is there a way to sort the list using the third character?

> > Thanks,
> > Chris

Re:Stringlist sorting


On Wed, 21 May 2003 09:11:19 GMT, "Chris Lock"

Quote
<chris.l...@kentdesigns.co.uk> wrote:
>Thanks all - but its a bit more complicated than that!

> Ive got a series of strings, each containing several fields separated
>by EndOfField and I'm displaying them in a stringgrid (Topgrid). I need
>to sort by column - date, integer, float etc.
>I was hoping there was an easy way to sort the underlying stringlist.
>Thanks for your ideas - I'll battle on.
>Chris

The CustomSort that Bj?rge suggested should do that

Unfortunately it is not implemented in my D4P

However, if your TStringList contains strings like :

     'alpha , 99999 , 2003-12-31 ,  more alpha'

Then repeatedly pulling out the sub-fields could be extremely
inefficient.  

You might be better off pulling the strings out into a dynamic array
of Records and sorting an array of Record Numbers

Re:Stringlist sorting


In article <XoHya.65942$UJ5.20...@news-lhr.blueyonder.co.uk>, "Chris Lock"

Quote
<chris.l...@kentdesigns.co.uk> writes:
>Thanks all - but its a bit more complicated than that!

> Ive got a series of strings, each containing several fields separated
>by EndOfField and I'm displaying them in a stringgrid (Topgrid). I need
>to sort by column - date, integer, float etc.
>I was hoping there was an easy way to sort the underlying stringlist.

This is how I sorted a stringgrid ...

The first type is to use as a typecast to expose the Move method of a
TStringGrid, the second is the type of the compare function.

type
  TGridSortCompare = function(Row1, Row2 : TStrings) : integer;
  {returns -1 if Row1 < Row2, 0 if Row1 = Row2, & +1 if Row1 > Row2}
  TMoveStrGrid = class(TCustomGrid);

The main sorting procedure,

procedure SortStringGrid(StringGrid : TStringGrid;
                         CompareProc : TGridSortCompare);
{*D 14/11/2000}
{sort a stringgrid}
var
  Sorted : boolean;
  SelRow, TopVisibleRow, i : integer;
begin
  with StringGrid do begin
    TopVisibleRow := TopRow;
    SelRow := Row;
    repeat {until Sorted}
      Sorted := true;
      for i := 2 to RowCount - 1 do begin // row 0 is a fixed row
        if CompareProc(Rows[i-1], Rows[i]) > 0 then begin
          {... then Row[i-1] is > Row[i] but should be <, therefore swap}
          Sorted := false;  // set flag for continuing to sort
          {swap stringgtid rows}
          TMoveStrGrid(StringGrid).MoveRow(i, i-1);
        end {if CompareProc(Rows[i-1], Rows[i]) > 0}
      end; {for i := 2 to RowCount - 1}
    until Sorted;
    TopRow := MinMaxGridRow(StringGrid, TopVisibleRow);
    Row := MinMaxGridRow(StringGrid, SelRow);
  end; {with StringGrid}
end;

You may not need this function, which ensures that the previously selected row
is still within view (I think <g>)

function MinMaxGridRow(Grid : TStringGrid; ARow : integer) : integer;
{*D 14/11/2000}
{ensures selected row is in range}
begin
  Result := ARow;
  with Grid do begin
    if ARow < FixedRows then
      Result := FixedRows;
    if ARow >= RowCount then
      Result := RowCount - 1;
  end;
end;

This  was a fairly complex sort which included some items which had to be at
the top regardless (VIP), but the comments after the function declaration
describe what you must return, write your code to do just that given the two
row values ...

function CompareGridRows(RowA, RowB : TStrings) : integer;
{each row is passed as a TStrings of the columns
 returns < 0 if RowA should be before RowB
          0 if they are the equal order
         > 0 if RowA should be after RowB
 used with, and specified in a call to, LexacomUtils.SortStringGrid
 caters for bringing all dictations of a VIP to top if VIPName specified}
var
  AisVIP, BisVIP : boolean;
begin
  Result := 0;
  if Length(VIPName) > 0 then begin
    AisVIP := (RowA[Text3Col] = VIPName);
    BisVIP := (RowB[Text3Col] = VIPName);
    {VIP before others}
    Result := integer(BisVIP) - integer(AisVIP); // false == boolean(0), true
== boolean(1)
  end;
  {high priority before lower priority}
  if (Result = 0) then
    Result := integer(RowB.Objects[PriorityCol])
            - integer(RowA.Objects[PriorityCol]);
  if (Result = 0) then
    {earlier dates before later dates for same priority / importance}
    Result := integer(RowA.Objects[DateTime2Col])
                     - integer(RowB.Objects[DateTime2Col]);
end;

Other Threads