I am using Spring4D for all collections.
Now there is a situation where I have to know whether the current value of the enumerator is the first (which is easy) or the last (which is hard) in the collection.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Spring.Collections;
var
Enumerable: IEnumerable<Integer>;
Enumerator: IEnumerator<Integer>;
begin
Enumerable := TEnumerable.Query<Integer>(TArray<Integer>.Create(1, 2, 3, 4, 5)
) as IEnumerable<Integer>;
Enumerator := Enumerable.GetEnumerator;
while Enumerator.MoveNext do
begin
WriteLn('Value = ', Enumerator.Current);
WriteLn('First in collection? ', Enumerator.CurrentIsFirst);
WriteLn('Last in collection? ', Enumerator.CurrentIsLast);
end;
ReadLn;
end.
CurrentIsFirst
could be implemented using a local Boolean which is reset once the first value has passed.
However I don't know an easy way to implement CurrentIsLast
.
It should be able to process lazy collections as they may contain too many values to fit into memory.
How can I implement such a CurrentIsLast
function?
Just use a flag during the iteration:
if Enumerator.MoveNext then
begin
flag := True;
repeat
WriteLn('Value = ', Enumerator.Current);
WriteLn('First in collection? ', flag);
flag := not Enumerator.MoveNext;
WriteLn('Last in collection? ', flag);
until flag;
end;
This is the basic algorithm but you can put that into a decorator for IEnumerator<T>
to provide IsFirst
/IsLast
- you just need to buffer the current element and look one ahead to see if the current one is the last.
type
IEnumeratorEx<T> = interface(IEnumerator<T>)
function IsFirst: Boolean;
function IsLast: Boolean;
end;
TEnumeratorState = (Initial, First, Only, Running, Last, Finished);
TEnumeratorEx<T> = class(TEnumeratorBase<T>, IEnumeratorEx<T>)
private
fSource: IEnumerator<T>;
fCurrent: T;
fState: TEnumeratorState;
function IsFirst: Boolean;
function IsLast: Boolean;
protected
function GetCurrent: T; override;
function MoveNext: Boolean; override;
public
constructor Create(const source: IEnumerator<T>);
end;
constructor TEnumeratorEx<T>.Create(const source: IEnumerator<T>);
begin
inherited Create;
fSource := source;
end;
function TEnumeratorEx<T>.GetCurrent: T;
begin
Result := fCurrent;
end;
function TEnumeratorEx<T>.IsFirst: Boolean;
begin
Result := fState in [First, Only];
end;
function TEnumeratorEx<T>.IsLast: Boolean;
begin
Result := fState in [Only, Last];
end;
function TEnumeratorEx<T>.MoveNext: Boolean;
begin
case fState of
Initial:
if fSource.MoveNext then
fState := First
else
fState := Finished;
First:
fState := Running;
Only, Last:
fState := Finished;
end;
Result := fState <> Finished;
if Result then
begin
fCurrent := fSource.Current;
if not fSource.MoveNext then
Inc(fState);
end;
end;