Is any way to add 2 arrays into one? Is any way to add 2 arrays into one? arrays arrays

Is any way to add 2 arrays into one?


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+Bprocedure Sum(const A,B: TPeople; var C: TPeople);beginvar 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+Bend;

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.


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.


There is a quick-and-dirty way to do this. It is a terrible hack, but it should work and even take care of reference counting:

function ConcatPeople(const A, B: TPeople): TPeople;var  Temp: TPeople;  ALen, BLen: Integer;begin  Result := Copy(A);  BLen := Length(B);  if BLen = 0 then    Exit;  ALen := Length(A);  Temp := Copy(B);  SetLength(Result, ALen + BLen);  Move(Temp[0], Result[ALen], BLen * SizeOf(B[0]));  FillChar(Temp[0], BLen * SizeOf(B[0]), 0);end;

In effect, the data in Temp are "swapped" with the empty records in Result, so the balance is maintained and refcounting will keep on working.

Update

For what it is worth: This is aparently the same technique as used in this accepted SO answer and in, e.g. TList<T>.Insert. I had deleted this answer, but I still think it is valid, so I undeleted it again. It could do with a lock around the Move/FillChar block, so no one accesses the items when they are being "swapped". I'll add that.