Delphi enumerate over TDataSet

Date: 2020-10-21

More about enumerators: https://www.thedelphigeek.com/2007/03/fun-with-enumerators.html

unit DataSet.Enumerator;

interface

uses DB;

{
Example:
var
  t: TDataSet;
begin
  for t in memTable1 do
    ShowMessage(t.FieldByName('Name').AsString + ' ' + t.FieldByName('Email').AsString);
end;
}

type
  TDataSetEnumerator = class
  private
      FDataSet: TDataSet;
      FDoFirst: boolean;
  public
      constructor Create(ADataSet: TDataSet);
      function GetCurrent: TDataSet;
      function MoveNext: boolean;
      property Current: TDataSet read GetCurrent;
  end;

  TDataSetHelper = class helper for TDataSet
  public
    function GetEnumerator: TDataSetEnumerator;
  end;

implementation

{ TDataSetEnumerator }

constructor TDataSetEnumerator.Create(ADataSet: TDataSet);
begin
  FDoFirst := True;
  FDataSet := ADataSet;
  FDataSet.Open;
end;

function TDataSetEnumerator.GetCurrent: TDataSet;
begin
  Result := FDataSet;
end;

function TDataSetEnumerator.MoveNext: boolean;
begin
  if FDoFirst then
  begin
    FDoFirst := False;
    FDataSet.First;
  end else
    FDataSet.Next;
  Result := not FDataSet.EOF;
end;

{ TDataSetHelper }

function TDataSetHelper.GetEnumerator: TDataSetEnumerator;
begin
  Result:= TDataSetEnumerator.Create(Self);
end;

end.

Example:

unit uMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
  FireDAC.DApt.Intf, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client, DataSet.Enumerator;

type
  TForm1 = class(TForm)
    memTable1: TFDMemTable;
    fieldName: TStringField;
    fieldEmail: TStringField;
    procedure FormCreate(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  t: TDataSet;
begin
  memTable1.Open;

  memTable1.Append;
  memTable1.FieldByName('Name').AsString := 'Test';
  memTable1.FieldByName('Email').AsString := 'test@test.com';
  memTable1.Post;

  memTable1.Append;
  memTable1.FieldByName('Name').AsString := 'Test 2';
  memTable1.FieldByName('Email').AsString := 'test 2@test.com';
  memTable1.Post;

  memTable1.Append;
  memTable1.FieldByName('Name').AsString := 'Test 3';
  memTable1.FieldByName('Email').AsString := 'test 3@test.com';
  memTable1.Post;

  for t in memTable1 do
    ShowMessage(t.FieldByName('Name').AsString + ' ' + t.FieldByName('Email').AsString);
end;

end.
uses
  System.Generics.Collections;


function Count<T>(AList: TEnumerable<T>): Integer;
var
  Enumerator: TEnumerator<T>;
begin
  Result := 0;
  Enumerator := AList.GetEnumerator;
  try
    while Enumerator.MoveNext do
      Inc(Result);
  finally
    Enumerator.Free;
  end;
end;

function First<T>(AList: TEnumerable<T>): T;
var
  Enumerator: TEnumerator<T>;
begin
  Enumerator := AList.GetEnumerator;
  try
    if Enumerator.MoveNext then
      Exit(Enumerator.Current)
    else
      raise Exception.Create('First<T>: Sequence contains no elements');
  finally
    Enumerator.Free;
  end;
end;

function Any<T>(AList: TEnumerable<T>): Boolean;
var
  Enumerator: TEnumerator<T>;
begin
  Enumerator := AList.GetEnumerator;
  try
    Result := Enumerator.MoveNext;
  finally
    Enumerator.Free;
  end;
end;

function ToArray<T>(AList: TEnumerable<T>): TArray<T>;
var
  List: TList<T>;
begin
  List := TList<T>.Create;
  try
    List.AddRange(AList);
    Result := List.ToArray;
  finally
    List.Free;
  end;
end;



uses
  System.SysUtils, System.Generics.Collections, System.Generics.Defaults;

type
  TEnumerableHelper<T> = record helper for TEnumerable<T>
    function Any: Boolean;
    function First: T;
    function ToArray: TArray<T>;
    function Count: Integer;
  end;

{ Implementatie }

function TEnumerableHelper<T>.Any: Boolean;
var
  Enumerator: TEnumerator<T>;
begin
  Enumerator := Self.GetEnumerator;
  try
    Result := Enumerator.MoveNext;
  finally
    Enumerator.Free;
  end;
end;

function TEnumerableHelper<T>.First: T;
var
  Enumerator: TEnumerator<T>;
begin
  Enumerator := Self.GetEnumerator;
  try
    if Enumerator.MoveNext then
      Exit(Enumerator.Current)
    else
      raise Exception.Create('First<T>: Sequence contains no elements');
  finally
    Enumerator.Free;
  end;
end;

function TEnumerableHelper<T>.ToArray: TArray<T>;
var
  TempList: TList<T>;
begin
  TempList := TList<T>.Create;
  try
    TempList.AddRange(Self);
    Result := TempList.ToArray;
  finally
    TempList.Free;
  end;
end;

function TEnumerableHelper<T>.Count: Integer;
var
  Enumerator: TEnumerator<T>;
begin
  Result := 0;
  Enumerator := Self.GetEnumerator;
  try
    while Enumerator.MoveNext do
      Inc(Result);
  finally
    Enumerator.Free;
  end;
end;

Usage

var
  List: TList<Integer>;
  FirstItem: Integer;
  Items: TArray<Integer>;
begin
  List := TList<Integer>.Create;
  try
    List.AddRange([5, 10, 15]);

    if List.Any then
      FirstItem := List.First;

    Items := List.ToArray;
  finally
    List.Free;
  end;
end;
41630cookie-checkDelphi enumerate over TDataSet