Is there way of copying the whole array into another array? (Other than using a For-loop)
Asked Answered
D

6

13

Is there way of copying the whole array into another array? Other than using a for-loop.

Does the move or copy command work for this? I did try but it had an error: "Incompatible types".

Should I stick to the for-loop?

Damnation answered 23/8, 2010 at 7:20 Comment(6)
Move uses a for loop. At least it did in Delphi 7. What code caused the error?Stavro
I didn't know that. Move and Copy both caused an error. (I didn't use them at the same time though)Damnation
Move doesn't uses a for loop. It's written in asm, even in Delphi 7, and use either a rep movsd/movsb with Delphi 7, or much faster FPU instructions in newer Delphi version.Feverish
If the arrays are of the same type and fixed (not dynamic) size then you can copy by simply assigning one to the other (array1 := array2).Ahoy
Unfortunately, it is a dynamic array. But I'll file that away for future reference. thanks. :DDamnation
Thanks for all the answers. It is appreciated (and upvoted). :DDamnation
P
25

To be on the safe side, use the Copy function on dynamic arrays, as it handles the managed types internally. The arrays must be of the same type, i.e. declared in the same expression:

var  
    a, b: array of string;

or by defining a custom array type:

type   
    TStringArray = array of string;  
var 
    a: TStringArray;  
//...and somewhere else
var  
    b: TStringArray;  

then you can do:

a := Copy(b, Low(b), Length(b));  //really clean, but unnecessary 
//...or   
a := Copy(b, 0, MaxInt);  //dynamic arrays always have zero low bound 
                          //and Copy copies only "up to" Count items  

You'll have to use a loop on static arrays and when mixing array types (not that I'd recommend doing it).
If you really have to use Move, remember checking for zero-length, as the A[0] constructs can raise range checking errors, (with the notable exception of SizeOf(A[0]), which is handled by compiler magic and never actually executes).
Also never assume that A = A[0] or SizeOf(A) = Length(A) * SizeOf(A[0]), as this is true only for static arrays and it will bite you really badly if you later try to refactor huge codebase to dynamic arrays.

Paraldehyde answered 23/8, 2010 at 13:45 Comment(2)
Although it's convenient to use hard-coded upper and lower bounds like 0 and MaxInt, it's even more convenient to omit the bounds entirely: a := Copy(b). That copies the entire array.Exequies
@Viktor, you might share the Copy(Source) trick with our Czech "MVP", as it seems he just discovered the possibility of omitting the last parameter (shame on me to accidentally visiting their blog).Solarium
U
6

See article on delphibasics.co.uk

You can copy an array using Copy method (pass in 0 for the index and Length(Source) as count to copy the full contents).

Do NOT use Move or CopyMemory for arrays of string/array/interface/etc managed types. Doing so will bypass Delphi's ref-counting mechanics and will result in memory leaks and corrupted data.

Unstick answered 23/8, 2010 at 8:24 Comment(1)
I think this small/simple answer is the best/most efficient answer.Intersect
F
6

For dynamic arrays:

var A,B: array of Byte;

begin
  SetLength(A, <size>);
  //initialize A

  B:= A; 
  SetLength(B,Length(A));
end;

In dynamic arrays, the assignment statement duplicates only the reference to the array, while SetLength does the job of physically copying/duplicating it, leaving two separate, independent dynamic arrays.

Fanatical answered 4/8, 2016 at 13:22 Comment(3)
Why not directly use: a := Copy(b, 0, MaxInt); ?Intersect
@Intersect which Delphi version extended LongString procedures (Insert, Delete, Copy and Slice, but AFAIR not AppendStr yet) to also accept syn-array instead of string? The version is the answer i guess. I wonder though if this indirect effect of SetLength is documented (is persistent contract) or merely some implementation detail?Cyanotype
@Arioch'The-SetLength works like this since Delphi 6.Intersect
F
3

1- If your array doesn't contain any string or dynamic array, you can use move, but dynamic arrays are not to be handled like fixed-sized arrays:

var A,B: array[0..10] of integer;
    DA, DB: array of double;
    i: integer;
begin
  for i := low(A) to high(A) do
    A[i] := i;
  move(A[0],B[0],length(A)*sizeof(A[0]));  // first version, compiler does the stuff
  move(A[0],B[0],sizeof(A)); // it works
  move(A[0],B[0],40); // if you know what you're doing, since sizeof(A)=40
  SetLength(DA,10); // DA[0]..DA[9]
  for i := 0 to high(DA) do // or for i := 0 to 9 if you know what you're doing
    DA[i] := 
  SetLength(DB,length(DA)); 
  if length(DA)<=length(DB) then // if your dynamic array may be void, use this before to avoid GPF
    move(DA[0],DB[0],length(DA)*sizeof(DA[0]));
  if pointer(DA)<>nil then // this will just check that DA[] is not void
    move(pointer(DA)^,pointer(DB)^,length(DA)*sizeof(double)); // similar to previous
end;

2- If your array contains strings or other reference content array, you have to use a loop:

var A,B: array[0..10] of string;
    i: integer;
begin
  for i := 0 to high(A) do
    A[i] := IntToStr(i);
  for i := 0 to high(A) do
    B[i] := A[i]; // this doesn't copy the string content, just add a reference count to every A[], and copy a pointer: it's very fast indeed.
end;
Feverish answered 23/8, 2010 at 9:51 Comment(1)
"if you know what you're doing, since sizeof(A)=40" It's 44.Po
G
0

I have a new version of the one I had above that doesn't satisfy the main question of avoiding a loop:

class function TGen.arrayCopy<T>(const a: array of T): TArray<T>;
begin
   SetLength(result, length(a));
   TArray.Copy<T>(a, result, length(a));
end;

TArray.Copy is declared in System.Generics.Collections;

Gag answered 26/8, 2024 at 5:40 Comment(0)
G
-1

You can use a record type that uses a generic function to copy arrays to a dynamic TArray variable, which I have started using:

 TGen = record // Unused record to allow generic functions.
 public
      ...
       class function arrayCopy<T>(const a: array of T): TArray<T>; static;
 end;



class function TGen.arrayCopy<T>(const a: array of T): TArray<T>;
var i: integer;
begin
  SetLength(result, length(a));
  for i := Low(a) to High(a) do
    result[i] := a[i];
end;

Given a form variable

dtls: TArray<TGridSetupDetails>;

and a parameter assigned from an array over an enumerated type

const adtls: array of TGridSetupDetails

you can initialize the form variable:

  dtls := TGen.arrayCopy<TGridSetupDetails>(adtls);
Gag answered 22/7, 2021 at 6:42 Comment(4)
This will be super slow for large arrays!Intersect
Gabriel: Yes, it is not optimized, but so far it has served me well, because in general I find my arrays are fairly short. It is at least clear; there are no hidden behaviours.Gag
Also, given the function signature TGen.arrayCopy<T>(const a: array of T): TArray<T>, the code result := copy(a, 0, length(a)); doesn't compile. If I change the parameter to const a: TArray<T>, it does not accept static arrays.Gag
The new code is this: class function TGen.arrayCopy<T>(const a: array of T): TArray<T>; begin SetLength(result, length(a)); TArray.Copy<T>(a, result, length(a)); end;Gag

© 2022 - 2025 — McMap. All rights reserved.