0

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;
6
  • 1
    You can't do this, these types just are not compatible. A dynamic array's metadata (i.e. ref count and length) is part of the object itself, but for an open array, its metadata (just the length) is passed as a separate parameter to the function. Commented Oct 28, 2021 at 10:27
  • Oh! That's so uncomfortable! I'll do two separated functions... Thanks Commented Oct 28, 2021 at 13:28
  • You can do it in one function easily enough. You just create copies, sort them, and then call the same function again recursively but passing True for AOrderMatters. I will write an answer to show how. Commented Oct 28, 2021 at 13:35
  • Yes it surely would be a cleaner code, but I was worrying about performances and I was trying to avoid copying the whole arrays if not strictly necessary Commented Oct 28, 2021 at 13:41
  • You have to copy the arrays if you are going to sort, but if you don't need to sort, then you can avoid the copy as I described, and as my answer demonstrates. Commented Oct 28, 2021 at 13:42

1 Answer 1

6

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, using a simple recursion. 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;
Sign up to request clarification or add additional context in comments.

Comments

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.