Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can array types have methods?

Tags:

arrays

delphi

I have defined a dynamic array type as follows:

TMyIntegerArray = array of integer:

I would like to use an IndexOf function as I would do if it were a TObject's descendant:

var
  MyArray : TMyIntegerArray;
  i : integer:
begin
  //...
  i := MyArray.IndexOf(10);
  //...
end;

At the moment, the only solution I've found is to write a function who accepts the array and the target value as parameters:

function IndexOf(AArray : TMyIntegerArray; ATargetValue : integer; AOffset : integer = 0);
begin
  Result := AOffset;
  while(Result < Length(AArray)) do
  begin
    if(AArray[Result] = ATargetValue)
    then Exit;
    Result := Result + 1;
  end;
  Result := -1;
end;

Can TMyIntegerArray type have a function like IndexOf?

Further Informations:

Currently, I'm using Delphi2007 but I'm also interested in knowing if there's any way to add methods to array types in newer Delphi's versions.

like image 443
Fabrizio Avatar asked Mar 13 '17 15:03

Fabrizio


3 Answers

In newer versions of Delphi (XE3+), it is possible to implement methods to array types with record helpers:

program ProjectTest;

{$APPTYPE CONSOLE}

Type
  TMyArray = array of integer;

  TMyArrayHelper = record helper for TMyArray
    procedure Print;
    function IndexOf(ATargetValue : integer; AOffset : integer = 0): Integer;
  end;

procedure TMyArrayHelper.Print;
var
  i: Integer;
begin
  for i in Self do WriteLn(i);  // Use Self for variable reference
end;

function TMyArrayHelper.IndexOf(ATargetValue : integer; AOffset : integer = 0): Integer;
begin
  Result := AOffset;
  while(Result < Length(Self)) do
  begin
    if(Self[Result] = ATargetValue)
    then Exit;
    Result := Result + 1;
  end;
  Result := -1;
end;

var
  myArr : TMyArray;
begin
  myArr := [0,1,2];  // A neat way to populate a dynamic array (XE7+)
  myArr.Print;
  WriteLn(myArr.IndexOf(2));

  ReadLn;
end.

Note: You can skip the TMyArray type declaration and use TArray<Integer> for a more relaxed type resolution. As always with record helpers, there can be only one helper attached to a type (and the one that will be used is the one nearest in scope).

This type of helper is called an intrinsic type helper, where the compiler puts an implicit record structure around the type.

like image 162
LU RD Avatar answered Nov 15 '22 07:11

LU RD


Although LU RD showed a direct solution to your question, I am going to add a slightly different approach based on generics. This has the advantage of providing a valid solution for different array types in one place.

For Delphi versions which supports generics, one can adopt the way used in TArray found in System.Generics.Collections. This is a straight forward extension of that class introducing a function IndexOf:

type
  TArrayExt = class(TArray)
  public
    class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index, Count:
        Integer): Integer; overload; static;
    class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Integer; overload;
        static;
    class function IndexOf<T>(const Values: array of T; const Item: T): Integer; overload; static;
  end;

class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index,
    Count: Integer): Integer;
var
  I: Integer;
begin
  if (Index < Low(Values)) or ((Index > High(Values)) and (Count > 0))
    or (Index + Count - 1 > High(Values)) or (Count < 0)
    or (Index + Count < 0) then
    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
  if Count = 0 then
  begin
    Exit(-1);
  end;
  for I := Index to Index + Count - 1 do begin
    if Comparer.Equals(Item, Values[I]) then begin
      Exit(I);
    end;
  end;
  Result := -1;
end;

class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Integer;
begin
  Result := IndexOf<T>(Values, Item, Comparer, Low(Values), Length(Values));
end;

class function TArrayExt.IndexOf<T>(const Values: array of T; const Item: T): Integer;
begin
  result := IndexOf<T>(Values, Item, TEqualityComparer<T>.Default, Low(Values), Length(Values));
end;

A simple use case could look like this:

procedure Main;
var
  arr: TArray<Integer>;
  N: Integer;
begin
  arr := TArray<Integer>.Create(5, 7, 3, 4, 2);
  repeat
    Readln(N);
    N := TArrayExt.IndexOf(arr, N);
    Writeln(N);
  until false;
end;
like image 24
Uwe Raabe Avatar answered Nov 15 '22 07:11

Uwe Raabe


For Delphi 2009 and above you can use a generic list:

uses System.Generics.Collections;

var
    MyArray : TList<integer>;
    i : integer;

begin
   MyArray := TList<integer>.Create;
   MyArray.Add(3);
   MyArray.Add(7);
   MyArray.Add(10);
   i := MyArray.IndexOf(10);
end;

In Delphi 2007 you might use a custom record:

type
   TMyArray = record
     private
      TheArray : array of integer;
     public
      procedure Add(Value : integer);
      function  IndexOf(Value : integer) : integer;
      function  Length : integer;
   end;   

procedure TMyArray.Add(Value : integer);

var
   i : integer;

begin
   i := length(TheArray);
   setlength(TheArray,i+1);
   TheArray[i] := Value; 
end;

function TMyArray.IndexOf(Value : integer) : integer;

var
   i : integer;

begin
   for i := 0 to length(TheArray)-1 do
      begin
         if TheArray[i] = Value then
            begin
               Result := i;
               exit; 
            end;
      end;
   Result := -1;
end;

function TMyArray.Length : integer;

begin
   Result := length(TheArray);
end;


procedure MyFunction;

  var
     MyArray : TMyArray;
     i : integer;

  begin
     MyArray.Add(3);
     MyArray.Add(7);
     MyArray.Add(10);
     i := MyArray.IndexOf(10);
  end;   
like image 23
Dave Olson Avatar answered Nov 15 '22 07:11

Dave Olson