unit Sortable;{$mode objfpc}{$H+}interface//Sortable- a nice array for records by domaszuses Classes, SysUtils, StdCtrls, FGL, Types, Dialogs, Generics.Collections, Generics.Defaults;type { TSortable } generic TSortable<TData> = class private type TCompareFun = function(const Data1, Data2: TData): Integer; private FItems: array of TData; FMaxSize: Integer; FSize: Integer; FSorted: Boolean; procedure SetItem(Index: Integer; Value: TData); function GetItem(Index: Integer): TData; procedure Grow; function BinSearch(Data: TData; CompareFun: TCompareFun): Integer; public constructor Create; property Count: Integer read FSize; property Items[Index: Integer]: TData read GetItem write SetItem; default; procedure Add(Value: TData); procedure Delete(Index: Integer); procedure Sort(CompareFun: TCompareFun); procedure Clear; function IndexOf(Data: TData; CompareFun: TCompareFun): Integer; procedure Optimize; end;implementation{ TSortable }procedure TSortable.SetItem(Index: Integer; Value: TData);begin if (Index<0) or (Index>FSize-1) then Exception.Create('Index out of bounds'); FItems[Index] := Value;end;function TSortable.GetItem(Index: Integer): TData;begin if (Index<0) or (Index>FSize-1) then Exception.Create('Index out of bounds'); Result := FItems[Index];end;procedure TSortable.Grow;begin Inc(FMaxSize, 100); SetLength(FItems, FMaxSize);end;function TSortable.BinSearch(Data: TData; CompareFun: TCompareFun): Integer;var Left, Right: Integer; Cur: TData; i: Integer; Found: Boolean; SameRes: Integer;begin Found := false; Result := -1; if FSize = 0 then Exit; Left := 0; Right := FSize-1; while ((CompareFun(FItems[Left], FItems[Right]) <= 0) and (Found = false)) do begin i := Left + ((Right - Left) div 2); Cur := FItems[i]; SameRes := CompareFun(Data, Cur); if SameRes = 0 then begin Result := i; Found := True; end else if SameRes > 0 then Left := i + 1 else Right := i - 1; end;end;constructor TSortable.Create;begin inherited Create; FMaxSize := 100; FSize := 0; FSorted := True; SetLength(FItems, FMaxSize);end;procedure TSortable.Add(Value: TData);begin if FSize = FMaxSize then Grow; FItems[FSize] := Value; Inc(FSize); FSorted := False;end;procedure TSortable.Delete(Index: Integer);var i: Integer;begin if (Index<0) or (Index>FSize-1) then Exception.Create('Index out of bounds'); for i:=Index to FSize-3 do FItems[i] := FItems[i+1]; Dec(FSize);end;procedure TSortable.Sort(CompareFun: TCompareFun); procedure QSort(ALo, AHi: Integer); var Lo, Hi: Integer; Pivot, Temp: TData; begin Lo := ALo; Hi := AHi; Pivot := FItems[(Lo + Hi) div 2]; repeat while CompareFun(FItems[Lo], Pivot) < 0 do Inc(Lo); while CompareFun(FItems[Hi], Pivot) > 0 do Dec(Hi); if Lo <= Hi then begin Temp := FItems[Lo]; FItems[Lo] := FItems[Hi]; FItems[Hi] := Temp; Inc(Lo) ; Dec(Hi) ; end; until Lo > Hi; if Hi > ALo then QSort(ALo, Hi) ; if Lo < AHi then QSort(Lo, AHi) ; end;begin QSort(0, FSize-1); FSorted := True;end;procedure TSortable.Clear;begin FSize := 0;end;function TSortable.IndexOf(Data: TData; CompareFun: TCompareFun): Integer;var i: Integer;begin if FSorted then begin Result := BinSearch(Data, CompareFun); Exit; end; Result := -1; for i:=0 to FSize-1 do if CompareFun(FItems[i], Data) = 0 then begin Result := i; Exit; end;end;procedure TSortable.Optimize;begin FMaxSize := FSize; SetLength(FItems, FSize);end;end.
Is there a generic that can be an array of record but with sorting by data?
generics.collections can do that