Question

In C# Linq would make this super-easy but I am new to using Lists in Delphi and I need some advice.

I have a list of objects stored as TList<IMyInterface> with each object essentially being a collection of data, e.g.

1, 1, 2, 2, 2, 2, 3, 4, 4, 4 

I wish to create a new TList<TList<IMyInterface>> where the items in the list are grouped such as:

1, 1 
2, 2, 2, 2, 2 
3 
4, 4, 4 

What would be the most effective way of doing this in Delphi XE3 (starter edition)?

Was it helpful?

Solution

The easiest would be a set of loops. Using the DeHL or Spring frameworks for Delphi might shorten your code a bit, but not much.

This question about querying generic TList instances using conditions gives a hint on using the Collections from the Spring framework.

My example uses Integer in stead of your interface type, but it should be easy to adapt.

Below are two methods for splitting, depending if you want each list to start on a changing value, or only for distinct values (like David said: long live TDictionary<Key, Value>)

I see the difference, I doubled your example:

1, 1, 2, 2, 2, 2, 3, 4, 4, 4, 1, 1, 2, 2, 2, 2, 3, 4, 4, 4

The first algorithm will return these:

1, 1
2, 2, 2, 2
3
4, 4, 4
1, 1
2, 2, 2, 2
3
4, 4, 4

The second these:

1, 1, 1, 1
2, 2, 2, 2, 2, 2, 2, 2
3, 3
4, 4, 4, 4, 4, 4

Here is the example program; only printing requires nested loops.

program SplitListOfIntegersIntoSublists;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Generics.Collections;

type
  TIntegerList = TList<Integer>;
  TIntegerListList = TList<TIntegerList>;
  TMain = class(TObject)
  private
    class function AddNewIntegerList(IntegerListList: TIntegerListList): TIntegerList;
    class procedure AddValues(AllIntegers: TIntegerList);
    class procedure Fill_NewListForEachValueChange(const AllIntegers: TIntegerList; const IntegerListList: TIntegerListList);
    class procedure Fill_NewListForDistinctValues(const AllIntegers: TIntegerList; const IntegerListList: TIntegerListList);
    class procedure Free(const IntegerListList: TIntegerListList);
    class procedure Print(const IntegerList: TIntegerList); overload;
    class procedure Print(const IntegerListList: TIntegerListList); overload;
  public
    class procedure Run;
  end;

class function TMain.AddNewIntegerList(IntegerListList: TIntegerListList): TIntegerList;
begin
  Result := TIntegerList.Create;
  IntegerListList.Add(Result);
end;

class procedure TMain.AddValues(AllIntegers: TIntegerList);
begin
// 1, 1, 2, 2, 2, 2, 3, 4, 4, 4
  AllIntegers.Add(1);
  AllIntegers.Add(1);
  AllIntegers.Add(2);
  AllIntegers.Add(2);
  AllIntegers.Add(2);
  AllIntegers.Add(2);
  AllIntegers.Add(3);
  AllIntegers.Add(4);
  AllIntegers.Add(4);
  AllIntegers.Add(4);
end;

class procedure TMain.Fill_NewListForEachValueChange(const AllIntegers: TIntegerList; const IntegerListList: TIntegerListList);
var
  IntegerList: TIntegerList;
  Value: Integer;
begin
  for Value in AllIntegers do
  begin
    if (IntegerListList.Count = 0) or (Value <> IntegerList.First) then
      IntegerList := AddNewIntegerList(IntegerListList);
    IntegerList.Add(Value);
  end;
end;

class procedure TMain.Fill_NewListForDistinctValues(const AllIntegers: TIntegerList; const IntegerListList:
    TIntegerListList);
type
  TIntegerListDictionary = TDictionary<Integer, TIntegerList>;
var
  IntegerListDictionary: TIntegerListDictionary;
  IntegerList: TIntegerList;
  Value: Integer;
begin
  IntegerListDictionary := TIntegerListDictionary.Create();
  for Value in AllIntegers do
  begin
    if IntegerListDictionary.ContainsKey(Value) then
      IntegerList := IntegerListDictionary[Value]
    else
    begin
      IntegerList := AddNewIntegerList(IntegerListList);
      IntegerListDictionary.Add(Value, IntegerList);
    end;
    IntegerList.Add(Value);
  end;
end;

class procedure TMain.Free(const IntegerListList: TIntegerListList);
var
  IntegerList: TIntegerList;
begin
  for IntegerList in IntegerListList do
    IntegerList.Free;
  IntegerListList.Free;
end;

class procedure TMain.Print(const IntegerList: TIntegerList);
var
  Value: Integer;
  First: Boolean;
begin
  First := True;
  for Value in IntegerList do
  begin
    if not First then
      Write(', ');
    Write(Value);
    First := False;
  end;
  Writeln;
end;

class procedure TMain.Print(const IntegerListList: TIntegerListList);
var
  IntegerList: TIntegerList;
begin
  for IntegerList in IntegerListList do
    Print(IntegerList);
  Writeln;
end;

class procedure TMain.Run;
var
  AllIntegers: TIntegerList;
  IntegerListList: TIntegerListList;
begin
  AllIntegers := TIntegerList.Create();
  try
    AddValues(AllIntegers);
    Print(AllIntegers);

    IntegerListList := TIntegerListList.Create();
    try
      Fill_NewListForEachValueChange(AllIntegers, IntegerListList);
      Print(IntegerListList);
    finally
      Free(IntegerListList);
    end;

    AddValues(AllIntegers);
    Print(AllIntegers);

    IntegerListList := TIntegerListList.Create();
    try
      Fill_NewListForEachValueChange(AllIntegers, IntegerListList);
      Print(IntegerListList);
    finally
      Free(IntegerListList);
    end;

    Print(AllIntegers);

    IntegerListList := TIntegerListList.Create();
    try
      Fill_NewListForDistinctValues(AllIntegers, IntegerListList);
      Print(IntegerListList);
    finally
      Free(IntegerListList);
    end;
  finally
    AllIntegers.Free;
  end;
end;

begin
  try
    TMain.Run();
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top