3

How to append one array to another array of the same type without using iterative statements (for or while loops) in Delphi?

5
  • SetLength() and Move() Commented Feb 15, 2016 at 10:32
  • 3
    Based on your comments to the answers, I believe your fear for loops might be caused by a misunderstanding of often seen statement "Do not concatenate strings into one string in loops" in the context of e.g. for i := 0 to NumOfStrings-1 do Result := Result + MyStrings[i] which indeed is slow and may fail totally in an OutOfMemory error, because it requires new and ever larger memory allocations for each turn of the loop. (continues ...) Commented Feb 15, 2016 at 13:58
  • 3
    (...continuation) Your question is about concatenating two array of string. Note that a string variable is a reference (or pointer if you like) to elsewhere allocated memory that holds the characters of the string. An array of string is therefore effectively an array of references. Concatenating two array of string only copies the references from one array to the other. The actual string data is not copied, it remains in the memory positions allocated before. Both answers have also resized the receiving array in one go, using SetLength(). Commented Feb 15, 2016 at 13:59
  • 1
    See How to merge 2 string array in Delphi. Commented Feb 15, 2016 at 14:31
  • 1
    Your insistence on avoiding iteration is a requirement that cannot be met. Commented Feb 15, 2016 at 15:38

3 Answers 3

12

In the last Delphi versions (XE7+) you can just use + operator or Concat routine to append arrays. Link. Official help (doesn't mention +)

Otherwise write your own procedure (use generic arrays if possible). Quick example (checked in XE3):

type 
TArrHelper = class
  class procedure AppendArrays<T>(var A: TArray<T>; const B: TArray<T>);
end;


class procedure TArrHelper.AppendArrays<T>(var A: TArray<T>;
  const B: TArray<T>);
var
  i, L: Integer;
begin
  L := Length(A);
  SetLength(A, L + Length(B));
  for i := 0 to High(B) do
    A[L + i] := B[i];
end;

usage:

var
  A, B: TArray<String>;
begin
  A := TArray<String>.Create('1', '2', '3');
  B := TArray<String>.Create('4', '5');
  TArrHelper.AppendArrays<String>(A, B);
Sign up to request clarification or add additional context in comments.

4 Comments

I need to use it in 2010
@D. Venkata Naresh Example for 2010 added
In the TArrHelper.AppendArrays(), for loop is there. I need to eliminate these loops. Because my array size is very huge. Is there any direct functions like ReplaceElements available?
What is your real problem? ANY method (including inbuilt concatenation and Move) will use some kind of loop intrinsically.
3

If you don't mind that your original array is destroyed, then there is a hackish solution. It is probably a lot faster than a loop, because each iteration of the loop must add one reference, while DestructiveConcatArrays preserves the reference count.

This means that copies of the strings to be moved are not allowed. They are either in Destination, or in Source, but can't be in both at the same time. Otherwise their refcounts would have to be updated anyway -- in a loop.

Note

Between the Move and the FillChar, all string references copied over are not properly refcounted. But after the FillChar, they are again. Care must be taken that nothing, no thread, should be able to access the Source array in that unstable state.

In other words: the following does not require the RTL to add or remove references, but it is tricky and it destroys the original (second) array:

procedure DestructiveConcatArrays(var Destination, Source: TArray<string>);
var
  LenD, LenS: Integer;
begin
  LenD := Length(Destination);
  if LenD = 0 then
    Destination := Source
  else
  begin
    LenS := Length(Source);
    if Length(Source) > 0 then
    begin
      SetLength(Destination, LenD + LenS);
      // Copy strings -- Afterwards, the refcounts of all strings copied over are  
      //                 out of sync.
      Move(Source[0], Destination[LenD], LenS * SizeOf(string));
      // Clear Source -- Afterwards, all refcounts are in sync again.
      FillChar(Source[0], LenS * SizeOf(string), 0);
    end;
  end;
  Source := nil;
end;

Careful!

The above is not a general solution. It is a hack, designed for this single purpose only. But it works as expected. I tested that. But it is not thread-safe, although it can probably be made to be.


Update

This is very much what C++ introduced with move semantics for rvalue expressions. Just consider the Source as an rvalue.

2 Comments

I'm not quite sure why this got the -1, considering it provides a useful alternative to other ways for going about it, so here's my +1.
@MartynA: thanks. It is a hack, but it works. These days, you can use System.CopyArray. But that also uses iteration, and updates every reference count.
2

Having two dynamic arrays arr1 and arr2

var
  arr1, arr2: array of Integer;
. . .
SetLength(arr1, 3);
arr1[0] := 1;
arr1[1] := 2;
arr1[2] := 3;

SetLength(arr2, 3);
arr2[0] := 4;
arr2[1] := 5;
arr2[2] := 6;

you can append the first to the second like this:

SetLength(arr2, Length(arr2) + Length(arr1));
Move(arr1[0], arr2[3], Length(arr1) * SizeOf(Integer));

See System.Move.


As Uwe Raabe's comment points out, you can do as follows for managed types:

SetLength(arr2, Length(arr2) + Length(arr1));
for i := Low(arr1) to High(arr1) do
  arr2[3+i] := arr1[i];

12 Comments

Note that Move is a bad choice when you have arrays of managed types like strings or interfaces.
how to append array of strings?
@fantaghirocco, I need to eliminate these loops. Because my array size is very huge. Is there any direct functions like ReplaceElements available?
there's no magic - Move itself is implemented as a loop - but why do you think that a loop isn't an efficient solution?
I you only want to concatenate things, probably thre's no reason to do it for real: you could simply keep track of the array sequence in an array of arrays like a3: array [0..x] of array of Pointer => [0] [@arr1, @arr2]. Just o give you an idea - it's better to use a custom pointer type
|

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.