Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grouping elements by GroupID in array or list?

I have made a simplified demo (with a pseudo structure) to illustrate what I need:

type
  TMyRec = record
    GroupID: Integer;
    Color: TColor;
  end;

  TMyRecArray = array of TMyRec;

My input array/list elements consists of non zero GroupIDs. They always grouped by GroupID (but not sorted and cannot be sorted):

GroupID
-------
2 
2
2
1
1
3
3
etc...

each element has a Color. My output should be grouped by GroupIDs and should look like this (Where each group is either clRed/clGreen - alternating in turn):

GroupID ; Color
-------   -----
2 ; clRed
2 ; clRed
2 ; clRed
1 ; clGreen
1 ; clGreen
3 ; clRed
3 ; clRed
etc...
9 ; clGreen    
7 ; clRed
7 ; clRed    

I use the code:

procedure TForm1.Button1Click(Sender: TObject);
var
  R: TMyRecArray;
  I: Integer;
  S: string;
  OldId: Integer;
begin
  // populate some data
  SetLength(R, 7);
  R[0].GroupID := 2;
  R[1].GroupID := 2;
  R[2].GroupID := 2;
  R[3].GroupID := 1;
  R[4].GroupID := 1;
  R[5].GroupID := 3;
  R[6].GroupID := 3;

  OldId := 0;
  for I := 0 to High(R) do
  begin
    if OldId <> R[I].GroupID then
    begin
      OldId := R[I].GroupID;
      R[I].Color := clRed;
    end
    else
    begin
      R[I].Color := clGreen;
    end;
  end;

  Memo1.Clear;
  for I := 0 to High(R) do
  begin
    ColorToIdent(R[I].Color, S);
    Memo1.Lines.Add(Format('%d ; %s', [R[I].GroupID, S]));
  end;
end;

And the result is NOT correct:

2 ; clRed
2 ; clGreen
2 ; clGreen
1 ; clRed
1 ; clGreen
3 ; clRed
3 ; clGreen

What am I missing? I know the solution is simple but I just can't put my mind on it.
Limitations: I can only iterate once; I cannot read ahead/previous records.

like image 209
ZigiZ Avatar asked Jan 30 '26 10:01

ZigiZ


1 Answers

"Divide and conquer." You can separate gathering of attributes and applying the coloring, from the decision making.

  OldId := 0; GroupNo := 0;
  for I := Low(R) to High(R) do
  begin
    if (OldId <> R[I].GroupId) or (GroupNo = 0) then
    begin
      OldId := R[I].GroupId;
      Inc(GroupNo);
      ItemNo := 0;
    end;
    Inc(ItemNo); 

    R[I].Color := GroupColor(OldId, GroupNo, ItemNo);
  end;

Then you can focus on the choice itself without overflowing your mind with details about actual coloring, data iteration and else. That also would give you more flexibility of what do you actually need. GroupColor may even be a variable, implementing different highlighting patterns according to setup.

Do you want alternating colors group after groups ?

  function GroupColor(const Id, Number, Item: integer): TColor;
  begin
    if Odd(Number) 
       then Result := clRed
       else Result := clGreen
  end;

Or do you want different groups have different colors ?

  function GroupColor(const Id, Number, Item: integer): TColor;
  begin
    case Id of
       1: Result := clRed;
       2: Result := clYellow;
       3: Result := clBlue;
       4: Result := clGreen;
       else Result := clWhite;
    end;
  end;

Or do you want to stress the tops, leaders of each group ?

  function GroupColor(const Id, Number, Item: integer): TColor;
  begin
    case Item of
       1: Result := clRed;
       2: Result := clYellow;
       3: Result := clGreen;
       else Result := clGray;
    end;
  end;
like image 119
Arioch 'The Avatar answered Feb 01 '26 03:02

Arioch 'The



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!