2

Im trying to sort Label's values. I have lots of labels with an integer value. Labels are called like Label1, Label2, [...], which Im accessing through FindComponent. I have no problem in sorting the integer values Ive stored in an array, but the problem is, after sorting, I have no idea which label had what value. My goal is to like, sort those labels by their value, so I'd get like an array with Labels sorted by their value. Im stuck at this point :( Eg:

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);

Now, I have sorted array, but Im lacking some sort procedure that would also store label number in the corresponding place. Can someone point me out?

4 Answers 4

1

Instead of creating an array of integers, create an array of TLabel controls. This one you can sort the same way as the array of integers. Indeed, given a MyLabel: TLabel, you can easily get the associated integer as StrToInt(MyLabel.Caption).

In addition, the FindComponent approach is not very efficient. I'd do

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.

Of course, you can skip the chunk allocating if you know the number of labels in advance.

Make sure that each of the labels that are to be included in the array have the Tag set to MAGIC_TAG.

Another option would be to create an array

FLabelDataArray: array of  TLabelData;

of

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

where

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

is computed only once.

Sign up to request clarification or add additional context in comments.

3 Comments

Am I the only one who thinks that sorting labels is nonsense? The labels should not be used for storage, IMO. One should use real storage (like an array or TList<T>) and sort that.
@Rudy: I completely agree. But I was given this question, and so I answered it. There isn't much more I can do.
@Rudy: You are right. I've added your wise suggestion to my answer.
1

A quick-n-dirty solution that also works in old Delphi versions, is to use TStringList, which has a Sort method and an Objects property that allow you to associate one object to each entry in the list.

Note that the list is sorted in lexicographic order, so the integers must be left padded with zeroes when converted to strings.

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;

The output would be:

0000000004   Label2
0000000007   Label3
0000000010   Label1

Also, if instead of list.Sort you use list.Sorted := true, you get binary search on list as a bonus (using list.IndexOf or list.Find).

Edit: As Rudy says, visual components such as TLabel should only be used for displaying data, and not for storing and manipulating it. It is recommended to use appropiate data structures for this and to separate the logic of the program from its user interface.

Comments

0

As Andreas says, put the labels into a dynamic array, rather than sorting the values. Once you have them in such an array, sort them like this:

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;

1 Comment

I am using delphi 7; cant find the Generics :(
0

As others have said, I don't think you're taking the right approach for this task. But, based on your question, a simple hack would be to use the tag property on each label to store its caption's value :

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

Then you can find the appropriate label by matching the 'array of integer' entry with the label's tag property.

Again: As Rudy and others have commented, your approach to this task is far from desirable, and my solution only conforms to your approach. The tag property itself is a hack built into Delphi, an artifact from ancient times, like 'label' and 'goTo' and really should not be used except out of sheer desperation - like when trying to re-work and retro-fit ancient, poorly written code or if you've got something in prod and you must get in a quick fix for an emergency situation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.