【问题标题】:Sorting a string list containing numbers and strings对包含数字和字符串的字符串列表进行排序
【发布时间】:2021-08-12 18:01:47
【问题描述】:

我正在尝试对一个充满分数和字符串的字符串网格进行排序,如下所示:

通过一个排序字符串列表,该列表按网格中的单个列排序,分别称为 SUB 1、SUB 2、FINAL、TOTAL(除 FINAL 外,所有这些列都有效),我在 FINAL 列中没有得到我需要的结果.

我试图让列按这样排序,例如:

24
20
12
5
DNF
EXE
WE

但我得到的是这个结果(我不想要的结果):

DNF
EXE
WE
24
20
12
5

我可以通过什么方式更改我的代码以按照我想要的方式对网格进行排序?

我的代码:

function Compare2(
    List   : TStringList;
    Index1 : Integer;
    Index2 : Integer) : Integer;
begin
  //comparer for custom sort used in SortLTSGrid
    if List[Index1] = List[Index2] then
        Result := 0
    else if List[Index1] < List[Index2] then
        Result := 1
    else
        Result := -1;
end;

procedure TfrmPuntehou.SortLTSGrid(var grid: TStringGrid; columntotal: Integer);
var
    TheList : TStringList;
    i,l,iCount,m:integer;
    const
    separator = ',';
const
    arrCodes:array[1..10] of string = ('DNF','DNS','WD','WE','DNA','OD','RD','EXR','EXE','PP');
begin
  //sorts grid largest to smallest according to one column

    //get grid row amount
    iCount:=grid.RowCount - 1;
    //create and fill the string list
    TheList := TStringList.Create;
      //fill the list
      for i := 1 to (iCount) do
      begin
        for l := 1 to Length(arrCodes) do
        begin
          if grid.Rows[i].Strings[columntotal] = arrCodes[l] then
          begin
          TheList.Add('0'+separator+grid.Rows[i].Text);
          end;
        end;
        TheList.Add(grid.Rows[i].Strings[columntotal]+separator+grid.Rows[i].Text);
      end;

    //try block to sort and write all strings in the list to the grid correctly
    try

       TheList.CustomSort(Compare2);

      for i:= 1 to (iCount) do
      begin
      grid.Rows[i].Text := TheList.Strings[(i-1)] ;
      end;

      //fill the row numbers
      for m := 1 to iCount do
      begin
      grid.Cells[0,m]:= IntToStr(m);
      end;


    finally
        TheList.Free;
    end;
end;

【问题讨论】:

  • 使用TryStrToInt 检测值是否为整数,如果是,请确保它们始终排在其他所有值之后。
  • @DavidHeffernan 我看到这个函数返回一个布尔值,那我应该检查真假吗?
  • 我很困惑。所有列都包含相同的元素。然而,它适用于除“决赛”之外的所有人吗?您想要与其他人不同的“决赛”顺序吗?在显示的表格中,敌人的例子,我看不出它如何适用于“Heat 2”而不适用于“Final”
  • @dsm 简而言之,是的。我也不确定为什么最后一栏充满了它。问题是我排序的所有其他列只有整数值,而 FINAL 中有字符串和整数。 SUB 和 TOTAL 列分别是总计/小计,FINAL 列代表特定组晚上的最后阶段/比赛(我正在改进我为当地的土椭圆赛车俱乐部编写的软件)。因此,可能有一个赛车手没有完成,例如,该赛车手列中的单元格中会有一个 DNF。所以那些参加比赛的人应该显示在那些没有参加比赛的人之上
  • 这就是我上面提到的问题所在

标签: sorting delphi tstringlist


【解决方案1】:

您可以使用两个不同的列表来存储项目,并使用两个不同的排序函数(因为您希望对它们进行不同方向的排序;数字将按降序排序,字符串将按升序排序)对列表进行排序。分别对列表进行排序,然后将它们合并。

请考虑@David Heffernan 的性能警告。

program Project3;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes;

var
 slStrings, slNumbers:TStringList;
 test:string;

function CompareForNumbers(
    List   : TStringList;
    Index1 : Integer;
    Index2 : Integer) : Integer;
 var
  val1, val2:Integer;
begin
  val1 := StrToInt(List[Index1]);
  val2 :=  StrToInt(List[Index2]);

  if val1 = val2 then
    Result := 0
  else if val1 < val2 then
    Result := 1
  else
    Result := -1;
end;

function CompareForStrings(
    List   : TStringList;
    Index1 : Integer;
    Index2 : Integer) : Integer;
begin
  if List[Index1] = List[Index2] then
    Result := 0
  else if List[Index1] > List[Index2] then
    Result := 1
  else
    Result := -1;
end;

begin
  slStrings := TStringList.Create();
  slNumbers := TStringList.Create();
  try
    slStrings.Add('EXE');
    slStrings.Add('WE');
    slStrings.Add('DNF');

    slNumbers.Add('5');
    slNumbers.Add('20');
    slNumbers.Add('24');
    slNumbers.Add('12');

    slNumbers.CustomSort(CompareForNumbers);
    slStrings.CustomSort(CompareForStrings);

    slNumbers.AddStrings(slStrings);

    Writeln(slNumbers.Text);

    Readln(test);
  finally
    slStrings.Free();
    slNumbers.Free();
  end;
end.

要使用单个列表来处理@David Heffernan 的性能警告,我写了这个;

program Project3;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes;

var
 slStrings:TStringList;
 test:string;


function Compare(
    List   : TStringList;
    Index1 : Integer;
    Index2 : Integer) : Integer;
 var
  val1, val2:Integer;
  val1integer, val2integer:Boolean;
begin
  val1integer := TryStrToInt(List[Index1], val1);
  val2integer := TryStrToInt(List[Index2], val2);

  if val1integer and val2integer then
    begin
      if val1 = val2 then
        Result := 0
      else if val1 < val2 then
        Result := 1
      else
        Result := -1;
    end
  else if (not val1integer) And (not val2integer) then
    begin
      if List[Index1] = List[Index2] then
        Result := 0
      else if List[Index1] > List[Index2] then
        Result := 1
      else
        Result := -1;
    end
  else
    begin
      if val1integer then
        Result := -1
      else
        Result := 1;
    end;
end;

begin
  slStrings := TStringList.Create();
  try
    slStrings.Add('EXE');
    slStrings.Add('5');
    slStrings.Add('WE');
    slStrings.Add('20');
    slStrings.Add('DNF');
    slStrings.Add('24');
    slStrings.Add('12');
    slStrings.Add('A');
    slStrings.Add('6');
    slStrings.Add('E');
    slStrings.Add('B');
    slStrings.Add('4');
    slStrings.Add('T');
    slStrings.Add('444');

    slStrings.CustomSort(Compare);

    Writeln(slStrings.Text);

    Readln(test);
  finally
    slStrings.Free();
  end;
end.

【讨论】:

  • TryStrToFloat 函数不起作用。德尔福说there is no overloaded version of TryStrToFloat that can be called with these arguments
  • AddStrings 也给了我同样的错误
  • 好的,所以我再次尝试了您发布的代码,它仍然适用于使用此过程的其他列(SUB 2 和 TOTAL),但它仍然给我与我之前提到的相同的结果在最后一列中,顶部的字符串和下方的整数(我试图实现相反的效果)。但是还是谢谢
  • 仍然没有变化,我得到相同的输出,顺便说一下 tmpFloat 会是什么(我没有看到它被分配到任何地方,尽管它被声明为变量),有趣的是我在我之前实际上尝试过发表了我之前的评论,哈哈
  • @ssdk 不,仍然没有区别。
【解决方案2】:

我相信您希望首先按最短到最长对时间进行排序,然后按字母顺序对所有文本进行排序(尽管这是有争议的)。

我不会像你那样修改文本。相反,我会简单地修改比较器函数并“按原样”传递文本。

为了测试它,我使用了 TMemo,但原理适用于表格 - 只需将相应的列复制到字符串列表。

function Compare2(
    List   : TStringList;
    Index1 : Integer;
    Index2 : Integer) : Integer;
var
  i1IsNumeric, i2IsNumeric : boolean;
  i1, i2 : integer;
begin
  //comparer for custom sort used in SortLTSGrid
  i1IsNumeric := TryStrToInt( List[Index1], i1 );
  i2IsNumeric := TryStrToInt( List[Index2], i2 );
  if i1IsNumeric and (not i2IsNumeric) then
  begin
    Result := -1;
  end
  else if i2IsNumeric and (not i1IsNumeric) then
  begin
    Result := 1;
  end
  else if i1IsNumeric then
  begin
    Result := Sign( i1-i2);
  end
  else
  begin
    Result := CompareStr( List[Index1], List[Index2] );
  end;

end;

这是我使用备忘录的测试例程

procedure TForm4.Button1Click(Sender: TObject);
var
  iList : TStringList;
begin
  iList := TStringList.Create;
  try
    iList.Assign( Memo1.Lines );
    iList.CustomSort( Compare2 );
    Memo1.Lines.Assign( iList );
  finally
    iList.Free;
  end;
end;

你的例程会更像(虽然我没有测试过)

procedure TfrmPuntehou.SortLTSGrid(var grid: TStringGrid; columntotal: Integer);
var
    TheList : TStringList;
    i,l,iCount,m:integer;
    const
    separator = ',';
const
    arrCodes:array[1..10] of string = ('DNF','DNS','WD','WE','DNA','OD','RD','EXR','EXE','PP');
begin
  //sorts grid largest to smallest according to one column

    //get grid row amount
    iCount:=grid.RowCount - 1;
    //create and fill the string list
    TheList := TStringList.Create;
      //fill the list
      for i := 1 to (iCount) do
      begin
        TheList.Add(grid.Rows[i].Text);
      end;
    //try block to sort and write all strings in the list to the grid correctly
    try

       TheList.CustomSort(Compare2);

      for i:= 1 to (iCount) do
      begin
      grid.Rows[i].Text := TheList.Strings[(i-1)] ;
      end;

      //fill the row numbers
      for m := 1 to iCount do
      begin
      grid.Cells[0,m]:= IntToStr(m);
      end;


    finally
        TheList.Free;
    end;
end;

【讨论】:

  • 所以我对此进行了测试,但这根本不起作用(也不适用于 SUB 2 或 TOTAL 列)。它把所有东西都弄乱了,而不是排序,但还是谢谢
  • 这个比较功能很好@PrimeBeat。如果你的代码没有做你想做的事,那是你的代码有问题,而不是这里的代码。
  • @Dsm 一个小问题,使用i1 - i2是不是风险溢出了?
  • @PrimeBeat 我发布的原始代码中存在错误,但现在已更正。
  • @DavidHeffernan,我想在理论上是这样,尽管在实践中它必须是一个涉及至少一个负时间的错误。如果所有时间都是积极的,它们应该是积极的,那么就不会。 OP 可以先检查一下。
猜你喜欢
  • 2021-02-22
  • 1970-01-01
  • 2017-08-11
  • 1970-01-01
  • 2016-10-28
  • 2022-11-23
  • 1970-01-01
  • 2021-08-02
  • 2016-01-23
相关资源
最近更新 更多