Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is any way to add 2 arrays into one?

Is there any simple univesal way to add 2 arrays into one? In the case below it is not possible simply use C := A + B statement... I would like to avoid making algorhytm for it everytime .

TPerson = record
    Birthday: Tdate;
    Name, Surname:string;
end;

Tpeople = array of TPerson;

var A, B, C:Tpeople;

C:=A+B; // it is not possible

thanx

like image 439
lyborko Avatar asked Aug 18 '11 05:08

lyborko


2 Answers

Due to the two string fields in each TPerson record, you can't just use binary "move", since you'll mess the reference counting of string - especially in a multi-threaded environment.

You can do it manually - this is fast and nice:

TPerson = record
  Birthday: TDate;
  Name, Surname: string;
end;

TPeople = array of TPerson;

var A, B, C: TPeople;

// do C:=A+B
procedure Sum(const A,B: TPeople; var C: TPeople);
begin
var i, nA,nB: integer;
begin
  nA := length(A);
  nB := length(B);
  SetLength(C,nA+nB);
  for i := 0 to nA-1 do
    C[i] := A[i];
  for i := 0 to nB-1 do
    C[i+nA] := B[i];
end;

Or you can use our TDynArray wrapper, which has a method for handling such cases:

procedure AddToArray(var A: TPeople; const B: TPeople);
var DA: TDynArray;
begin
  DA.Init(TypeInfo(TPeople),A);
  DA.AddArray(B); // A := A+B
end;

The AddArray method can add a sub-port of the original array:

/// add elements from a given dynamic array
// - the supplied source DynArray MUST be of the same exact type as the
// current used for this TDynArray
// - you can specify the start index and the number of items to take from
// the source dynamic array (leave as -1 to add till the end)
procedure AddArray(const DynArray; aStartIndex: integer=0; aCount: integer=-1);

Note that with such records, it will use the System._CopyRecord RTL function, which is not so optimized for speed. I've written a faster version - see this blog article or this forum thread.

If you use dynamic arrays in functions/procedures, don't forget to use explicitly const or var parameters (as I coded above), otherwise it will make a temporary copy at each call, therefore it may be slow.

like image 84
Arnaud Bouchez Avatar answered Oct 21 '22 10:10

Arnaud Bouchez


There is nothing built in that allows dynamic arrays to be concatenated.

You may consider using one of the generic container classes found in Generics.Collections, TList.

In your case you would have 3 instances of TList, say A, B and C. Then you could write

A.Clear;
A.AddRange(B);
A.AddRange(C);

I think this is as close as you can get to what you want with what is delivered out of the box.

If you are prepared to do a bit of coding yourself then you could make use of operator overloading to use the exact syntax you requires. Declare a record containing an array of TPerson with private visibility. You then need to implement an Add operator, a Count property and a default Items[] property. This could be made generic too so you only need write it once.

TTurboArray = record<T>
private
  FItems: array of T;
  //property accessors here
 public
   class operator Add(a, b: TTurboArray<T>): TTurboArray<T>;
   property Count: Integer read GetCount write SetCount;
   property Items[Index: Integer]: T read GetItem write SetItem; default;
end;

This idea can be extended into a very powerful data structure as you see fit.

like image 45
David Heffernan Avatar answered Oct 21 '22 10:10

David Heffernan