Сортировка значений меток Delphi - PullRequest
2 голосов
/ 31 июля 2011

Я пытаюсь отсортировать значения Лейбла.У меня есть много меток с целочисленным значением.Метки называются как Label1, Label2, [...], к которым я обращаюсь через FindComponent.У меня нет проблем с сортировкой целочисленных значений, которые я сохранил в массиве, но проблема в том, что после сортировки я понятия не имею, какая метка имела какое значение.Моя цель состоит в том, чтобы сортировать эти метки по их значению, чтобы получить массив с метками, отсортированными по их значению.Я застрял в этой точке :( Например:

Label1.Caption := 10;
Label2.Caption := 4;
Label3.Caption := 7;

for i := 1 to 3
 do some_array[i] := StrToInt(TLabel(FindComponent('Label' + IntToStr(i))).Caption);

sortarray(some_array);

Теперь я отсортировал массив, но мне не хватает какой-то процедуры сортировки, которая бы также сохраняла номер метки в соответствующем месте. Может кто-нибудь указать мне?

Ответы [ 4 ]

1 голос
/ 31 июля 2011

Быстрое решение, которое также работает в старых версиях Delphi, заключается в использовании TStringList, который имеет метод Sort и свойство Objects, которое позволяет вам связать один объект с каждой записью в список.

Обратите внимание, что список отсортирован в лексикографическом порядке, поэтому при преобразовании в строки целые числа должны быть дополнены нулями.

var
  list: TStringList;
  i: integer;
  lab: TLabel;
begin
  Label1.Caption := '10';
  Label2.Caption := '4';
  Label3.Caption := '7';

  list := TStringList.Create;
  try
    for i := 1 to 3 do begin
      lab := TLabel(FindComponent('Label' + IntToStr(i)));
      list.AddObject(Format('%10.10d', [StrToInt(lab.Caption)]), lab);
    end;

    list.Sort;

    for i := 0 to list.Count-1 do
      Memo1.Lines.Add(list[i] + #9 + TLabel(list.Objects[i]).Name);
  finally
    list.Free;
  end;
end;

Вывод будет:

0000000004   Label2
0000000007   Label3
0000000010   Label1

Также, если вместо list.Sort вы используете list.Sorted := true, вы получите бинарный поиск по list в качестве бонуса (используя list.IndexOf или list.Find).

Редактировать: Как говорит Руди , визуальные компоненты, такие как TLabel, должны использоваться только для отображения данных, а не для их хранения и манипулирования. Для этого рекомендуется использовать подходящие структуры данных и отделить логику программы от ее пользовательского интерфейса.

1 голос
/ 31 июля 2011

Вместо создания массива целых чисел, создайте массив элементов управления TLabel. Этот вы можете отсортировать так же, как массив целых чисел. В самом деле, учитывая MyLabel: TLabel, вы можете легко получить соответствующее целое число как StrToInt(MyLabel.Caption).

Кроме того, подход FindComponent не очень эффективен. Я бы сделал

const
  ALLOC_BY = 100;
  MAGIC_TAG = 871226;
var
  i: Integer;
  ActualLength: integer;
  FLabels: array of TLabel;
begin
  SetLength(FLabels, ALLOC_BY);
  ActualLength := 0;
  for i := 0 to ControlCount - 1 do
    if Controls[i] is TLabel then
      with TLabel(Controls[i]) do
        if Tag = MAGIC_TAG then
        begin
          if ActualLength = length(FLabels) then
            SetLength(FLabels, length(FLabels) + ALLOC_BY);
          FLabels[ActualLength] := Controls[i];
          inc(ActualLength);
        end;
  SetLength(FLabels, ActualLength);

  SortArray(FLabels) // with respect to the StrToInt(CurLabel.Caption) of each
                     // CurLabel: TLabel.

Конечно, вы можете пропустить выделение чанка, если заранее знаете количество меток.

Убедитесь, что для каждой из меток, которые должны быть включены в массив, для Tag установлено значение MAGIC_TAG.

Другой вариант - создать массив

FLabelDataArray: array of  TLabelData;

из

type
  TLabelData = record
    Control: TLabel;
    Value: integer;
  end;

, где

FLabelDataArray[i].Value := StrToInt(FLabelDataArray[i].Control.Caption);

вычисляется только один раз.

0 голосов
/ 01 августа 2011

Как уже говорили другие, я не думаю, что вы подходите к этой задаче правильно. Но, основываясь на вашем вопросе, простым взломом будет использование свойства тега на каждой метке для хранения значения заголовка:

Label1.Caption := '10';
Label1.Tag:=10;
Label2.Caption := '4';
Label2.Tag:=4;
Label3.Caption := '7';
Label3.Tag := 7;

Затем вы можете найти соответствующую метку, сопоставив запись «целочисленного» со свойством тега метки.

Снова: Как прокомментировали Руди и другие, ваш подход к этой задаче далеко не желателен, и мое решение соответствует только вашему подходу. Само свойство тега является хаком, встроенным в Delphi, артефактом с древних времен, таким как «label» и «goTo», и его действительно не следует использовать, кроме как из чистого отчаяния - например, при попытке переделать и заново установить древний, плохо написанный код или если у вас что-то есть в prod, и вы должны быстро исправить ситуацию в чрезвычайной ситуации.

0 голосов
/ 31 июля 2011

Как говорит Андреас, помещайте метки в динамический массив, а не сортируйте значения.После того, как они у вас есть в таком массиве, отсортируйте их так:

uses
  Generics.Defaults, Generics.Collections;

procedure SortLabels(var Labels: array of TLabel);
var
  Comparison: TComparison<TLabel>;
  Comparer: IComparer<TLabel>;
begin
  Comparison := function(const Left, Right: TLabel): Integer
    begin
      Result := StrToInt(Left.Caption)-StrToInt(Right.Caption);
    end;
  Comparer := TDelegatedComparer<TLabel>.Create(Comparison);
  TArray.Sort<TLabel>(Labels, Comparer);
end;
...