6

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.

1
  • My recommendation is that you should put your array inside a record. Not only can you give it methods, but you'll have scoping rules to assist in the proper encapsulation of your internal structure. Commented Mar 13, 2017 at 15:24

3 Answers 3

16

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.

Sign up to request clarification or add additional context in comments.

Comments

6

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;

5 Comments

TArray is a class to begin with, and is not compatible with a dynamic array directly. This solution does not answer the OP's question about adding methods to a dynamic array type directly.
@Remy, no, it doesn't, but it achieves the same. This "adding methods to a dynamic array type" question looks like an XY problem to me, and then, this is a good and valid answer, IMO. It also has the advantage of being generic for all kinds of arrays, and not limited to one single array type, unlike the accepted answer.
@Rudy But when the question explicitly speaks to adding instance methods to a type, I think the answer should at least address that. Even if it then goes on to offer alternative approaches. This is after all a Q&A site.
@RemyLebeau, can you show an example where this is not compatible with dynamic arrays? I tried (in 10.1) with TArray<Integer>, array of Integer and TIntegerDynArray.
@David: OK, a one liner addressing that could be prepended. <g>
2

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;   

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.