StringGrid でソートをしたい(3)

さて、最終回になる予定の第3回です。
ソートといっても、降順ソート、数値ソート、大/小文字区別ソート等いろいろと考えられます。
これらを実装しようという話です。

type
  TCompare = function(const S1, S2: string): Integer;

function StrCompAsNum(const Str1, Str2: string): Integer;
var
  Num1, Num2: Extended;
begin
  if not TextToFloat(PChar(Str1), Num1, fvExtended) then Num1 := 0.0;
  if not TextToFloat(PChar(Str2), Num2, fvExtended) then Num2 := 0.0;

  if Num1 = Num2 then Result := 0
  else if Num1 < Num2 then Result := -1
  else Result := 1;
end;

function StrCompAsNumRev(const Str1, Str2: string): Integer;
begin
  Result := -StrCompAsNum(Str1, Str2);
end;

function CompareStrRev(const Str1, Str2: string): Integer;
begin
  Result := -CompareStr(Str1, Str2);
end;

procedure TForm1.Sort(ACol: Integer);
  procedure MergeSort(Buffer: TStringList; ARow, Count: Integer; Compare: TCompare);
  var
    I, J: Integer;
    Center: Integer;
    Temp: TStringList;
  begin
    if Count = 1 then Exit;

    Center := Count div 2;

    MergeSort(Buffer, ARow, Center, Compare);
    MergeSort(Buffer, ARow + Center, Count - Center, Compare);

    I := 0;
    J := 0;
    Temp := TStringList.Create;
    while (I < Center) and (J < Count - Center) do
    begin
      if Compare(Buffer[ARow + I], Buffer[ARow + Center + J]) > 0 then
      begin
        Temp.AddObject(Buffer[ARow + Center + J], Buffer.Objects[ARow + Center + J]);
        Inc(J);
      end
      else
      begin
        Temp.AddObject(Buffer[ARow + I], Buffer.Objects[ARow + I]);
        Inc(I);
      end;
    end;

    if I = Center then
      while J < Count - Center do
      begin
        Temp.AddObject(Buffer[ARow + Center + J], Buffer.Objects[ARow + Center + J]);
        Inc(J);
      end
    else
      while I < Center do
      begin
        Temp.AddObject(Buffer[ARow + I], Buffer.Objects[ARow + I]);
        Inc(I);
      end;

    for I := 0 to Count - 1 do
    begin
      Buffer[ARow + I] := Temp[I];
      Buffer.Objects[ARow + I] := Temp.Objects[I];
    end;

    Temp.Free;
  end;

var
  S: string;
  ARow: Integer;
  Buffer: TStringList;
  Compare: TCompare;
begin
  with StringGrid1 do
  begin
    Buffer := TStringList.Create;

    //Buffer に key とそれに対応する Rows を格納する
    for ARow := TopRow to RowCount - 1 do
    begin
      if CaseSensitive then S := Cells[ACol, ARow]
      else AnsiLowerCase(Cells[ACol, ARow]);

      Buffer.AddObject(S, TStringList.Create);
      TStringList(Buffer.Objects[ARow - TopRow]).Assign(Rows[ARow]);
    end;

    if Reverse then
    begin
      if Numerical then Compare := StrCompAsNumRev else Compare := CompareStrRev;
    end
    else
    begin
      if Numerical then Compare := StrCompAsNum else Compare := CompareStr;
    end;

    //Buffer を実際にソートする
    MergeSort(Buffer, 0, RowCount - TopRow, Compare); //Buffer.Sort; と置き換え

    //ソートしたデータを Grid に書き戻す
    for ARow := TopRow to RowCount - 1 do
    begin
      Rows[ARow].Assign(TStringList(Buffer.Objects[ARow - TopRow]));
      TStringList(Buffer.Objects[ARow - TopRow]).Free;
    end;

    Buffer.Free;
  end;
end;

まあ、そう難しいことをやってるわけじゃないのでソースを比較すれば分かるでしょう。
CaseSensitive が大/小文字を区別するか、Numerical が数値ソートをするか、Reverse が降順ソートをするかということです。
しかし・・・結構長いソースだな。

あ、関数ってポインタで引き渡せるんですよ(笑)
知らなかった人、そういうものなんです(苦笑)


Return index page