Project: Array Utilities Unit
Unit: DelphiDabbler.Lib.ArrayUtils
Record: TArrayUtils
Applies to: ~>0.1
type
TConstraint<T> = reference to function (const AElem: T): Boolean;
TConstraintEx<T> = reference to function (const AElem: T;
const AIndex: Integer; const AArray: array of T): Boolean;
class function FindAllIndices<T>(const A: array of T;
const AConstraint: TConstraint<T>): TArray<Integer>;
overload; static;
class function FindAllIndices<T>(const A: array of T;
const AConstraint: TConstraintEx<T>): TArray<Integer>;
overload; static;Finds the indices of all the elements of an array for which a given constraint function returns True.
Parameters:
-
A - Array to be checked.
-
AConstraint - Constraint function called for each element of A. Returns
Trueif the element meets the required criteria orFalseotherwise. AConstraint must be a function of type TConstraint<T> or TConstraintEx<T>.Parameter(s):
- AElem - The current array element to be tested.
- AIndex - The index of AElem in A. (TConstraintEx<T> only.)
- AArray - Reference to the array containing AElem. (TConstraintEx<T> only.)
Returns:
Trueif AElem meets the required criteria,Falseotherwise.
Returns:
- Array of indices of all the elements for which AConstraint returns True. Will be empty if no such elements exist.
The TConstraint<T> overload is all you need for most purposes. However there are cases where it is useful to have access to the whole array or the element's index within the array, which is the reason the TConstraintEx<T> overload is provided.
The first example finds the indices of all the elements of an integer array that are ≥ 4. We only need the simple TConstraint<T> overload.
procedure FindAllIndices_Eg1;
var
A, B, Got, Expected: TArray<Integer>;
Constraint: TArrayUtils.TConstraint<Integer>;
begin
Constraint := function (const AElem: Integer): Boolean
begin
Result := AElem >= 4;
end;
A := TArray<Integer>.Create(1, 2, 3, 4, 5, 3);
Got := TArrayUtils.FindAllIndices<Integer>(A, Constraint);
Expected := TArray<Integer>.Create(3, 4);
Assert(TArrayUtils.Equal<Integer>(Expected, Got));
B := TArray<Integer>.Create(1, 2, 3);
Got := TArrayUtils.FindAllIndices<Integer>(B, Constraint);
Expected := TArray<Integer>.Create();
Assert(TArrayUtils.Equal<Integer>(Expected, Got));
end;The second example finds the indices of all the local maxima in an array of integers. To do this we need to be able to access the array element before and after a given element. Therefore the TConstraintEx<T> overload of FindAllIndices<T> is required.
procedure FindAllIndices_Eg2;
var
IsLocalMaxElem: TArrayUtils.TConstraintEx<Integer>;
A, B, Got, Expected: TArray<Integer>;
begin
IsLocalMaxElem :=
function (const AElem: Integer; const AIndex: Integer;
const A: array of Integer): Boolean
begin
if Length(A) = 0 then
// no local maxima in an empty array
Exit(False);
if Length(A) = 1 then
// the only element in a 1 element array is considered to be a maximum
Exit(True);
// Length(A) >= 2
if (AIndex = 0) then
// 1st elem: peak if next elem smaller
Result := A[Succ(AIndex)] < AElem
else if AIndex = Pred(Length(A)) then
// last elem: peak if previous elem smaller
Result := A[Pred(AIndex)] < AElem
else
// not 1st or last: peak if > than elems on either side
Result := (A[Succ(AIndex)] < AElem) and (A[Pred(AIndex)] < AElem);
end;
A := TArray<Integer>.Create(1, 2, 3, 2, 3, 5, 1);
Got := TArrayUtils.FindAllIndices<Integer>(A, IsLocalMaxElem);
Expected := TArray<Integer>.Create(2, 5);
Assert(TArrayUtils.Equal<Integer>(Expected, Got));
B := TArray<Integer>.Create(1, 1, 1, 1);
Got := TArrayUtils.FindAllIndices<Integer>(B, IsLocalMaxElem);
Expected := TArray<Integer>.Create();
Assert(TArrayUtils.Equal<Integer>(Expected, Got));
end;