Home > Software design >  TStringList.CustomSort: Compare() with variables
TStringList.CustomSort: Compare() with variables

Time:02-03

I am trying to custom sort a TStringList by a column in a .CSV file. My code below works (slowly, about 14 seconds for 200,000 lines):

function Compare(List: TStringList; Index1, Index2: Integer): Integer;

    function ColStr(const Ch: Char; const S: String; First, Last: Integer): String;
    var
        p1, p2: Integer;

        function GetPos(const N: Integer; Start: Integer = 1): Integer;
        var
            I, Len, Count: Integer;
        begin
            Result := 0;
            Len := Length(S);
            if (Len = 0) or (Start > Len) or (N < 1) then Exit;

            Count := 0;
            for I := Start to Len do begin
                if S[I] = Ch then begin
                    Inc(Count);
                    if Count = N then begin
                        Result := I;
                        Exit;
                    end;
                end;
            end;
        end;

    begin
        p1 := GetPos(4, 1); // 4 should be a variable
        p2 := GetPos(5, 1); // 5 should be a variable
        if Last = 0 then Result := Copy(S, p1   1, length(S)) else Result := Copy(S, p1   1, p2 - p1 - 1);
    end;

begin
    Result := AnsiCompareStr(ColStr(',', List[Index1], 0, 1), ColStr(',', List[Index2], 0, 1));
end;

What I would want to do is not have this hard-coded but (where commented "should be a variable" depending on which column to sort). I know I can't use:

function Form1.Compare(List: TStringList; Index1, Index2: Integer): Integer;

for inserting variables, as I get the error:

Incompatible types: 'method pointer and regular procedure'.

I have searched through SO looking for instances of this error but cannot find one that fits my question. I would appreciate any pointers in the right direction.

This has to be done with Delphi 7 and Windows 11.

CodePudding user response:

So you are performing searching for k-th occurence of Ch and substring creation at every comparison.

You can optimize this process - before sorting make list/array of stringlists, created from every string, separated by needed character - use DelimitedText.

Inside compare function just work with this array and column numbers - sadly, you have to define them as global variables in current unit (for example, after Form1: TForm1)

CodePudding user response:

TStringList.CustomSort() does not let you pass in extra parameters, and it does not accept class methods or anonymous procedures. But, what it does do is pass the actual TStringList itself to the callback, so I would suggest deriving a new class from TStringList to add extra fields to it, and then you can access those fields inside the callback, eg:

class TSortableStringList = class(TStringList)
public
  Count1: Integer;
  Count2: Integer;
end;

function Compare(List: TStringList; Index1, Index2: Integer): Integer;
    ...
    p1 := GetPos(TSortableStringList(List).Count1, 1);
    p2 := GetPos(TSortableStringList(List).Count2, 1);
    ...
begin
  ...
end;

...

List := TSortableStringList.Create;
// fill List ...
List.Count1 := ...;
List.Count2 := ...;
List.CustomSort(Compare);
  • Related