有没有办法在一个数组中添加两个数组?

时间:2023-01-27 23:48:56

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 .

有没有一种简单的方法可以在一个数组中添加两个数组?在以下情况下,仅使用C:= A + B语句是不可能的……我不想每次都用algorhytm。

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

谢谢

5 个解决方案

#1


11  

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.

由于每个TPerson记录中有两个字符串字段,所以不能只使用二进制“move”,因为这样会打乱字符串的引用计数—特别是在多线程环境中。

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:

或者您也可以使用我们的TDynArray包装器,它有处理此类情况的方法:

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:

AddArray方法可以添加原数组的子端口:

/// 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.

注意,如果有这样的记录,它将使用这个系统。_CopyRecord RTL函数,它没有对速度进行优化。我已经写了一个更快的版本-看这篇博客文章或这个论坛帖子。

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.

如果在函数/过程中使用动态数组,不要忘记显式地使用const或var参数(如上所示),否则每次调用时它都会临时复制,因此可能会很慢。

#2


3  

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.

您可以考虑使用泛型中发现的通用容器类之一。集合,TList。

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

在你的例子中,你会有3个TList实例,比如A, B和c,然后你可以写

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.

如果您准备自己编写一些代码,那么您可以使用操作符重载来使用所需的语法。声明包含具有私有可见性的TPerson数组的记录。然后需要实现一个Add操作符、一个Count属性和一个默认项[]属性。这也可能是通用的,所以你只需要写一次。

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.

这个想法可以扩展到一个非常强大的数据结构中。

#3


1  

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.

实际上,Temp中的数据与空记录进行了“交换”,因此保持了余额,而refcount将继续工作。

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.

值得注意的是:这与在这个通用的SO和in中使用的技术完全相同,例如TList . insert。我已经删除了这个答案,但是我仍然认为它是有效的,所以我再次删除了它。它可以在Move/FillChar块周围设置一个锁,因此在“交换”项时没有人访问它们。我将添加。

#4


1  

Here's how I handled it, though it required a slight (but hopefully immaterial to you) modification to your original code to use TArray:

这是我的处理方法,虽然它需要对你的原始代码做一个轻微的修改(但希望是不重要的),使用TArray:

(tested in XE2)

(在XE2测试)

uses
  Generics.Collections;

type
  TArrayExt = class(TArray)
    class function Concat<T>(const First, Second: array of T): TArray<T>; overload;
  end;

class function TArrayExt.Concat<T>(const First, Second: array of T): TArray<T>;
var
  i: Integer;
  l: Integer;
begin
  l := Length(First);
  SetLength(Result, l + Length(Second));
  for i := Low(First) to High(First) do
    Result[i] := First[i];
  for i := Low(Second) to High(Second) do
    Result[l + i] := Second[i];
end;

type
  TPerson = record
    Birthday: TDate;
    Name, Surname: String;
  end;

  TPeople = TArray<TPerson>;

var
  A, B, C: TPeople;
begin
  C := TArrayExt.Concat<TPerson>(A, B);

The main difference here is that I use "TArray" rather than "array of TPerson". This can be used for arrays strings, records, etc. I find the main benefit of doing it this way is that it's truly making a copy rather than a move. And I am using the "normal" Delphi functions instead of things like bulk memory copies, which can give me the willies.

这里的主要区别是我使用“TArray”而不是“TPerson数组”。这可以用于数组字符串、记录等。我发现这样做的主要好处是,它实际上是复制而不是移动。而我使用的是“正常”的Delphi函数,而不是像大容量内存拷贝那样的东西,这会让我感到焦虑。

Of course, if you were doing this in a tight loop and needed the performance, this way might not be best for you. But I think this is the best for most other situations, especially in terms of maintenance and readability.

当然,如果您在一个紧密的循环中执行此操作,并且需要性能,那么这种方法可能并不适合您。但我认为这对大多数其他情况来说是最好的,尤其是在维护和可读性方面。

(Unless someone posts a comment about how there's some horrible hidden memory leak here. Hopefully not!)

(除非有人发表评论说这里有一些可怕的隐藏内存泄漏。)希望不是!)

#5


0  

You code works fine in the newest version of delphi C := A+B;.

您的代码在最新版本的delphi C:= A+B中运行良好。

But for dynamic arrays in older versions you can use the function concat. Example:

但是对于旧版本中的动态数组,可以使用函数concat。例子:

C := Concat(A, B);

#1


11  

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.

由于每个TPerson记录中有两个字符串字段,所以不能只使用二进制“move”,因为这样会打乱字符串的引用计数—特别是在多线程环境中。

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:

或者您也可以使用我们的TDynArray包装器,它有处理此类情况的方法:

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:

AddArray方法可以添加原数组的子端口:

/// 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.

注意,如果有这样的记录,它将使用这个系统。_CopyRecord RTL函数,它没有对速度进行优化。我已经写了一个更快的版本-看这篇博客文章或这个论坛帖子。

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.

如果在函数/过程中使用动态数组,不要忘记显式地使用const或var参数(如上所示),否则每次调用时它都会临时复制,因此可能会很慢。

#2


3  

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.

您可以考虑使用泛型中发现的通用容器类之一。集合,TList。

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

在你的例子中,你会有3个TList实例,比如A, B和c,然后你可以写

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.

如果您准备自己编写一些代码,那么您可以使用操作符重载来使用所需的语法。声明包含具有私有可见性的TPerson数组的记录。然后需要实现一个Add操作符、一个Count属性和一个默认项[]属性。这也可能是通用的,所以你只需要写一次。

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.

这个想法可以扩展到一个非常强大的数据结构中。

#3


1  

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.

实际上,Temp中的数据与空记录进行了“交换”,因此保持了余额,而refcount将继续工作。

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.

值得注意的是:这与在这个通用的SO和in中使用的技术完全相同,例如TList . insert。我已经删除了这个答案,但是我仍然认为它是有效的,所以我再次删除了它。它可以在Move/FillChar块周围设置一个锁,因此在“交换”项时没有人访问它们。我将添加。

#4


1  

Here's how I handled it, though it required a slight (but hopefully immaterial to you) modification to your original code to use TArray:

这是我的处理方法,虽然它需要对你的原始代码做一个轻微的修改(但希望是不重要的),使用TArray:

(tested in XE2)

(在XE2测试)

uses
  Generics.Collections;

type
  TArrayExt = class(TArray)
    class function Concat<T>(const First, Second: array of T): TArray<T>; overload;
  end;

class function TArrayExt.Concat<T>(const First, Second: array of T): TArray<T>;
var
  i: Integer;
  l: Integer;
begin
  l := Length(First);
  SetLength(Result, l + Length(Second));
  for i := Low(First) to High(First) do
    Result[i] := First[i];
  for i := Low(Second) to High(Second) do
    Result[l + i] := Second[i];
end;

type
  TPerson = record
    Birthday: TDate;
    Name, Surname: String;
  end;

  TPeople = TArray<TPerson>;

var
  A, B, C: TPeople;
begin
  C := TArrayExt.Concat<TPerson>(A, B);

The main difference here is that I use "TArray" rather than "array of TPerson". This can be used for arrays strings, records, etc. I find the main benefit of doing it this way is that it's truly making a copy rather than a move. And I am using the "normal" Delphi functions instead of things like bulk memory copies, which can give me the willies.

这里的主要区别是我使用“TArray”而不是“TPerson数组”。这可以用于数组字符串、记录等。我发现这样做的主要好处是,它实际上是复制而不是移动。而我使用的是“正常”的Delphi函数,而不是像大容量内存拷贝那样的东西,这会让我感到焦虑。

Of course, if you were doing this in a tight loop and needed the performance, this way might not be best for you. But I think this is the best for most other situations, especially in terms of maintenance and readability.

当然,如果您在一个紧密的循环中执行此操作,并且需要性能,那么这种方法可能并不适合您。但我认为这对大多数其他情况来说是最好的,尤其是在维护和可读性方面。

(Unless someone posts a comment about how there's some horrible hidden memory leak here. Hopefully not!)

(除非有人发表评论说这里有一些可怕的隐藏内存泄漏。)希望不是!)

#5


0  

You code works fine in the newest version of delphi C := A+B;.

您的代码在最新版本的delphi C:= A+B中运行良好。

But for dynamic arrays in older versions you can use the function concat. Example:

但是对于旧版本中的动态数组,可以使用函数concat。例子:

C := Concat(A, B);