Home > Software design >  How to make a local array variable conditionally points to an open array parameter?
How to make a local array variable conditionally points to an open array parameter?

Time:10-29

I'm trying to conditionally assign a local open array variable in order it points to a const open array parameter:

uses
  System.Generics.Collections,
  System.Generics.Defaults;
    
type
  TArray = class(System.Generics.Collections.TArray)
  public
    class function  SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean; overload; static;
  end;
    
...
    
class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
  ArrA : TArray<T>;
  ArrB : TArray<T>;
  i : integer;
begin
  //checking sizes
  if(Length(AValuesA) <> Length(AValuesB)) then
  begin
    Result := False;
    Exit;
  end;

  if(AOrderMatters) then
  begin
    //I don't need to change the arrays, so I could directly point to the open array parameters
    ArrA := AValuesA;
    ArrB := AValuesB;
  end else
  begin
    //copying to local arrays
    SetLength(ArrA, Length(AValuesA));
    TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
    SetLength(ArrB, Length(AValuesB));
    TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));

    //sorting local arrays
    TArray.Sort<T>(ArrA);
    TArray.Sort<T>(ArrB);
  end;

  //comparing elements
  i := 0;
  while(i < Length(ArrA)) do
  begin
    if(not AComparer.Equals(ArrA[i], ArrB[i])) then
    begin
      Result := False;
      Exit;
    end;
    Inc(i);
  end;
  Result := True;
end;

On compiling, it raises E2010 error at:

//I don't need to change the arrays, so I could directly point to the open array parameters
ArrA := AValuesA;
ArrB := AValuesB;

E2010 Incompatible types: 'Dynamic array' and 'array of T'

I've tried the following:

ArrA := @AValuesA;
ArrB := @AValuesB;

It compiles but then it raises AV exception at runtime (and most of all, I don't know if it would be a safe approach).

Here is my test application code:

uses
  System.Generics.Defaults;

procedure TForm1.FormCreate(Sender: TObject);
var
  ArrA : array of string;
  ArrB : array of string;
begin
  SetLength(ArrA, 2);
  ArrA[0] := 'hello';
  ArrA[1] := 'world';

  SetLength(ArrB, 2);
  ArrB[0] := 'world';
  ArrB[1] := 'hello';

  if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, True)) then
    ShowMessage('Same values and same order')
  else if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, False)) then
    ShowMessage('Same values but different order')
  else
    ShowMessage('Different values');
end;

CodePudding user response:

What you are trying to do is not possible because open arrays and dynamic arrays are not compatible in the way that your code attempts.

There is an easy way to achieve what you want though. You can use a simple recursion to achieve what you need. Like this:

class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
  ArrA : TArray<T>;
  ArrB : TArray<T>;
  i : integer;
begin
  //checking sizes
  if Length(AValuesA) <> Length(AValuesB) then
  begin
    Result := False;
    Exit;
  end;

  if not AOrderMatters then
  begin
    //copying to local arrays
    SetLength(ArrA, Length(AValuesA));
    TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
    SetLength(ArrB, Length(AValuesB));
    TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));

    //sorting local arrays
    TArray.Sort<T>(ArrA);
    TArray.Sort<T>(ArrB);

    Result := SameValues<T>(ArrA, ArrB, AComparer, True);
    Exit;
  end;

  //comparing elements
  for i := 0 to High(AValuesA) do
  begin
    if not AComparer.Equals(AValuesA[i], AValuesB[i]) then
    begin
      Result := False;
      Exit;
    end;
  end;

  Result := True;
end;
  • Related