Quick.Linq.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. { ***************************************************************************
  2. Copyright (c) 2016-2022 Kike Pérez
  3. Unit : Quick.Linq
  4. Description : Arrays and Generic Lists Linq functions
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 04/04/2019
  8. Modified : 27/01/2022
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Linq;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. SysUtils,
  26. Generics.Collections,
  27. Generics.Defaults,
  28. RTTI,
  29. Quick.RTTI.Utils,
  30. Quick.Expression,
  31. Quick.Commons,
  32. Quick.Value,
  33. {$IFDEF FPC}
  34. Quick.Value.RTTI,
  35. {$ELSE}
  36. System.RegularExpressions,
  37. {$ENDIF}
  38. Quick.Arrays;
  39. type
  40. TOrderDirection = (odAscending, odDescending);
  41. {$IFNDEF FPC}
  42. TLinqExpression<T : class> = class(TExpression)
  43. private
  44. fPredicate : TPredicate<T>;
  45. public
  46. constructor Create(aPredicate : TPredicate<T>);
  47. function Validate(const aValue : TValue) : Boolean; override;
  48. function IsNull : Boolean; override;
  49. end;
  50. {$ENDIF}
  51. ILinqArray<T> = interface
  52. ['{3133DCAB-06C5-434B-B169-B32DC8C6308B}']
  53. function Any : Boolean; overload;
  54. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  55. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
  56. function OrderAscending : ILinqArray<T>;
  57. function OrderDescending : ILinqArray<T>;
  58. function SelectFirst : T;
  59. function SelectLast : T;
  60. function SelectTop(aLimit : Integer) : TArray<T>;
  61. function Select : TArray<T>; overload;
  62. function Count : Integer;
  63. function Update(const aNewValue : T) : Integer;
  64. function Delete : Integer;
  65. end;
  66. {$IFNDEF FPC}
  67. TLinqArray<T> = class(TInterfacedObject,ILinqArray<T>)
  68. type
  69. TLinqComparer = class(TInterfacedObject,IComparer<T>)
  70. private
  71. fSortAscending : Boolean;
  72. public
  73. constructor Create(aSortAscending : Boolean);
  74. property SortAscending : Boolean read fSortAscending;
  75. function Compare(const L, R: T): Integer;
  76. end;
  77. private
  78. fArray : TArray<T>;
  79. fMatchString : string;
  80. fUseRegEx : Boolean;
  81. function Validate(aValue : T) : Boolean;
  82. public
  83. constructor Create(aArray : TArray<T>);
  84. function Any : Boolean; overload;
  85. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  86. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
  87. function OrderAscending : ILinqArray<T>;
  88. function OrderDescending : ILinqArray<T>;
  89. function SelectFirst : T;
  90. function SelectLast : T;
  91. function SelectTop(aLimit : Integer) : TArray<T>;
  92. function Select : TArray<T>; overload;
  93. function Count : Integer;
  94. function Update(const aNewValue : T) : Integer;
  95. function Delete : Integer;
  96. end;
  97. TLinqArrayHelper = record helper for TArray<string>
  98. function Add(const aValue : string) : Integer;
  99. function AddIfNotExists(const aValue : string) : Integer;
  100. function Remove(const aValue : string) : Boolean;
  101. function Any : Boolean; overload;
  102. function Any(const aValue : string) : Boolean; overload;
  103. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  104. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>; overload;
  105. end;
  106. {$ENDIF}
  107. ILinqQuery<T> = interface
  108. ['{16B68C0B-EA38-488A-99D9-BAD1E8560E8E}']
  109. function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
  110. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  111. {$IFNDEF FPC}
  112. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  113. {$ENDIF}
  114. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  115. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  116. function SelectFirst : T;
  117. function SelectLast : T;
  118. function SelectTop(aLimit : Integer) : TxArray<T>;
  119. function Select : TxArray<T>; overload;
  120. function Select(const aPropertyName : string) : TFlexArray; overload;
  121. function Count : Integer;
  122. function Update(const aFields : array of string; aValues : array of const) : Integer;
  123. function Delete : Integer;
  124. end;
  125. TLinqQuery<T : class> = class(TInterfacedObject,ILinqQuery<T>)
  126. private type
  127. arrayOfT = array of T;
  128. TArrType = (atArray, atXArray, atList, atObjectList);
  129. private
  130. fWhereClause : TExpression;
  131. fOrderBy : TArray<string>;
  132. fOrderDirection : TOrderDirection;
  133. fPList : Pointer;
  134. fList : arrayOfT;
  135. fArrType : TArrType;
  136. function FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  137. procedure DoOrderBy(vArray : ArrayOfT);
  138. function Compare(const aPropertyName : string; L, R : T) : Integer;
  139. procedure Clear;
  140. public
  141. {$IFNDEF FPC}
  142. constructor Create(aObjectList : TObjectList<T>); overload;
  143. {$ENDIF}
  144. constructor Create(aList : TList<T>); overload;
  145. constructor Create(aXArray : TxArray<T>); overload;
  146. constructor Create(aArray : TArray<T>); overload;
  147. destructor Destroy; override;
  148. function Where(const aWhereClause : string; aWhereParams : array of const) : ILinqQuery<T>; overload;
  149. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  150. {$IFNDEF FPC}
  151. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  152. {$ENDIF}
  153. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  154. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  155. function SelectFirst : T;
  156. function SelectLast : T;
  157. function SelectTop(aLimit : Integer) : TxArray<T>;
  158. function Select : TxArray<T>; overload;
  159. function Select(const aPropertyName : string) : TFlexArray; overload;
  160. function Count : Integer;
  161. function Update(const aFields : array of string; aValues : array of const) : Integer;
  162. function Delete : Integer;
  163. end;
  164. TLinq<T : class> = class
  165. public
  166. {$IFNDEF FPC}
  167. class function From(aObjectList : TObjectList<T>) : ILinqQuery<T>; overload;
  168. {$ENDIF}
  169. class function From(aArray : TArray<T>) : ILinqQuery<T>; overload;
  170. class function From(aXArray : TXArray<T>) : ILinqQuery<T>; overload;
  171. end;
  172. ELinqNotValidExpression = class(Exception);
  173. ELinqError = class(Exception);
  174. implementation
  175. { TLinqQuery<T> }
  176. procedure TLinqQuery<T>.Clear;
  177. begin
  178. SetLength(fOrderBy,0);
  179. end;
  180. constructor TLinqQuery<T>.Create(aArray: TArray<T>);
  181. begin
  182. Clear;
  183. fPList := Pointer(aArray);
  184. fList := aArray;
  185. fArrType := TArrType.atArray;
  186. end;
  187. {$IFNDEF FPC}
  188. constructor TLinqQuery<T>.Create(aObjectList: TObjectList<T>);
  189. begin
  190. Clear;
  191. fPList := Pointer(aObjectList);
  192. {$IFDEF DELPHIRX104_UP}
  193. fList := aObjectList.PList^;
  194. {$ELSE}
  195. fList := aObjectList.List;
  196. {$ENDIF}
  197. fArrType := TArrType.atObjectList;
  198. end;
  199. {$ENDIF}
  200. constructor TLinqQuery<T>.Create(aXArray: TxArray<T>);
  201. begin
  202. Clear;
  203. fPList := Pointer(aXArray);
  204. fList := aXArray.PArray^;
  205. fArrType := TArrType.atXArray;
  206. end;
  207. constructor TLinqQuery<T>.Create(aList: TList<T>);
  208. begin
  209. Clear;
  210. fPList := Pointer(aList);
  211. {$IFDEF DELPHIRX104_UP}
  212. fList := aList.PList^;
  213. {$ELSE}
  214. fList := aList.ToArray;
  215. {$ENDIF}
  216. fArrType := TArrType.atList;
  217. end;
  218. function TLinqQuery<T>.Compare(const aPropertyName: string; L, R: T): Integer;
  219. var
  220. valueL : TValue;
  221. valueR : TValue;
  222. begin
  223. Result := 0;
  224. valueL := TRTTI.GetPathValue(L,aPropertyName);
  225. valueR := TRTTI.GetPathValue(R,aPropertyName);
  226. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(1)
  227. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(-1);
  228. case valueL.Kind of
  229. {$IFNDEF FPC}
  230. tkString,
  231. {$ENDIF}
  232. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  233. tkInteger, tkInt64 :
  234. begin
  235. if valueL.AsInteger > valueR.AsInteger then Result := 1
  236. else if valueL.AsInteger < valueR.AsInteger then Result := -1;
  237. end;
  238. tkFloat :
  239. begin
  240. if valueL.AsExtended > valueR.AsExtended then Result := 1
  241. else if valueL.AsExtended < valueR.AsExtended then Result := -1;
  242. end;
  243. end;
  244. end;
  245. function TLinqQuery<T>.Count: Integer;
  246. var
  247. i : Integer;
  248. begin
  249. Result := 0;
  250. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  251. for i := High(fList) downto Low(fList) do
  252. begin
  253. if fWhereClause.Validate(fList[i]) then
  254. begin
  255. Inc(Result);
  256. end;
  257. end;
  258. end;
  259. function TLinqQuery<T>.Delete: Integer;
  260. var
  261. i : Integer;
  262. begin
  263. Result := 0;
  264. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  265. for i := High(fList) downto Low(fList) do
  266. begin
  267. if fWhereClause.Validate(fList[i]) then
  268. begin
  269. case fArrType of
  270. TArrType.atArray, TArrType.atXArray :
  271. begin
  272. TObject(fList[i]).Free;
  273. System.Delete(fList,i,1);
  274. //fPList := Pointer(fList);
  275. end;
  276. TArrType.atList :
  277. begin
  278. TList<T>(fPList).Delete(i);
  279. end;
  280. TArrType.atObjectList :
  281. begin
  282. TObjectList<T>(fPList).Delete(i);
  283. end;
  284. end;
  285. Inc(Result);
  286. end;
  287. end;
  288. end;
  289. destructor TLinqQuery<T>.Destroy;
  290. begin
  291. if Assigned(fWhereClause) then fWhereClause.Free;
  292. inherited;
  293. end;
  294. procedure TLinqQuery<T>.DoOrderBy(vArray : ArrayOfT);
  295. begin
  296. if High(fOrderBy) < 0 then Exit;
  297. {$IFNDEF FPC}
  298. TArray.Sort<T>(vArray, TComparer<T>.Construct(
  299. function (const A, B: T): Integer
  300. var
  301. field : string;
  302. begin
  303. for field in fOrderBy do
  304. begin
  305. Result := Compare(field,A,B);
  306. if Result <> 0 then Break;
  307. end;
  308. if fOrderDirection = TOrderDirection.odDescending then Result := Result * -1;
  309. end)
  310. );
  311. {$ENDIF}
  312. end;
  313. function TLinqQuery<T>.OrderBy(const aFieldNames: string): ILinqQuery<T>;
  314. begin
  315. Result := Self;
  316. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  317. fOrderBy := aFieldNames.Split([',']);
  318. fOrderDirection := TOrderDirection.odAscending;
  319. end;
  320. function TLinqQuery<T>.OrderByDescending(const aFieldNames: string): ILinqQuery<T>;
  321. begin
  322. Result := Self;
  323. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  324. fOrderBy := aFieldNames.Split([',']);
  325. fOrderDirection := TOrderDirection.odDescending;
  326. end;
  327. function TLinqQuery<T>.Select(const aPropertyName: string): TFlexArray;
  328. var
  329. obj : T;
  330. value : TFlexValue;
  331. begin
  332. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  333. for obj in fList do
  334. begin
  335. if fWhereClause.Validate(obj) then
  336. begin
  337. //value := TRTTI.GetProperty(obj,aPropertyName);
  338. {$IFNDEF FPC}
  339. value := TRTTI.GetPathValue(obj,aPropertyName).AsVariant;
  340. {$ELSE}
  341. value.AsTValue := TRTTI.GetPathValue(obj,aPropertyName);
  342. {$ENDIF}
  343. Result.Add(value);
  344. end;
  345. end;
  346. end;
  347. function TLinqQuery<T>.Select: TxArray<T>;
  348. var
  349. obj : T;
  350. begin
  351. {$If Defined(FPC) OR Defined(DELPHIRX102_UP)}
  352. Result := [];
  353. {$ELSE}
  354. Result := nil;
  355. {$ENDIF}
  356. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  357. for obj in fList do
  358. begin
  359. if fWhereClause.Validate(obj) then Result.Add(obj);
  360. end;
  361. DoOrderBy(Result);
  362. end;
  363. function TLinqQuery<T>.SelectFirst: T;
  364. var
  365. obj : T;
  366. begin
  367. {$IFNDEF FPC}
  368. Result := nil;
  369. {$ENDIF}
  370. DoOrderBy(fList);
  371. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  372. for obj in fList do
  373. begin
  374. if fWhereClause.Validate(obj) then Exit(obj);
  375. end;
  376. end;
  377. function TLinqQuery<T>.SelectLast: T;
  378. var
  379. obj : T;
  380. begin
  381. {$IFNDEF FPC}
  382. Result := nil;
  383. {$ENDIF}
  384. DoOrderBy(fList);
  385. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  386. for obj in fList do
  387. begin
  388. if fWhereClause.Validate(obj) then Result := obj;
  389. end;
  390. end;
  391. function TLinqQuery<T>.SelectTop(aLimit: Integer): TxArray<T>;
  392. var
  393. obj : T;
  394. i : Integer;
  395. begin
  396. {$If Defined(FPC) OR Defined(DELPHIRX102_UP)}
  397. Result := [];
  398. {$ELSE}
  399. Result := nil;
  400. {$ENDIF}
  401. DoOrderBy(fList);
  402. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  403. i := 0;
  404. for obj in fList do
  405. begin
  406. if fWhereClause.Validate(obj) then
  407. begin
  408. Result.Add(obj);
  409. Inc(i);
  410. if i > aLimit then Exit;
  411. end;
  412. end;
  413. end;
  414. function TLinqQuery<T>.Update(const aFields: array of string; aValues: array of const): Integer;
  415. var
  416. obj : T;
  417. i : Integer;
  418. {$IFDEF FPC}
  419. value : TValue;
  420. {$ENDIF}
  421. begin
  422. Result := 0;
  423. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  424. for obj in fList do
  425. begin
  426. {$IFNDEF FPC}
  427. if obj = nil then continue;
  428. {$ELSE}
  429. if Pointer(obj) = nil then continue;
  430. {$ENDIF}
  431. if fWhereClause.Validate(obj) then
  432. begin
  433. for i := Low(aFields) to High(aFields) do
  434. begin
  435. if not TRTTI.PropertyExists(TypeInfo(T),aFields[i]) then raise ELinqError.CreateFmt('Linq update field "%s" not found in obj',[aFields[i]]);
  436. try
  437. {$IFNDEF FPC}
  438. TRTTI.SetPropertyValue(obj,aFields[i],aValues[i]);
  439. {$ELSE}
  440. case aValues[i].VType of
  441. vtString : value := string(aValues[i].VString^);
  442. vtChar : value := string(aValues[i].VChar);
  443. {$IFDEF MSWINDOWS}
  444. vtAnsiString : value := AnsiString(aValues[i].VAnsiString);
  445. vtWideString : value := WideString(aValues[i].VWideString);
  446. {$ENDIF}
  447. {$IFDEF UNICODE}
  448. vtUnicodeString: AsString := string(aValues[i].VUnicodeString);
  449. {$ENDIF UNICODE}
  450. vtInteger : value := aValues[i].VInteger;
  451. vtInt64 : value := aValues[i].VInt64^;
  452. vtExtended : value := aValues[i].VExtended^;
  453. vtBoolean : value := aValues[i].VBoolean;
  454. else raise Exception.Create('DataType not supported by Linq update');
  455. end;
  456. TRTTI.SetPropertyValue(obj,aFields[i],value);
  457. {$ENDIF}
  458. except
  459. on E : Exception do raise ELinqError.CreateFmt('Linq update error: %s',[e.Message]);
  460. end;
  461. end;
  462. Inc(Result);
  463. end;
  464. end;
  465. end;
  466. function TLinqQuery<T>.FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  467. var
  468. i : Integer;
  469. begin
  470. Result := aWhereClause;
  471. if aWhereClause = '' then
  472. begin
  473. Result := '1 = 1';
  474. Exit;
  475. end;
  476. for i := 0 to aWhereClause.CountChar('?') - 1 do
  477. begin
  478. case aWhereParams[i].VType of
  479. vtInteger : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInteger),[]);
  480. vtInt64 : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInt64^),[]);
  481. vtExtended : Result := StringReplace(Result,'?',FloatToStr(aWhereParams[i].VExtended^),[]);
  482. vtBoolean : Result := StringReplace(Result,'?',BoolToStr(aWhereParams[i].VBoolean),[]);
  483. vtAnsiString : Result := StringReplace(Result,'?',string(aWhereParams[i].VAnsiString),[]);
  484. vtWideString : Result := StringReplace(Result,'?',string(aWhereParams[i].VWideString^),[]);
  485. {$IFNDEF NEXTGEN}
  486. vtString : Result := StringReplace(Result,'?',string(aWhereParams[i].VString^),[]);
  487. {$ENDIF}
  488. vtChar : Result := StringReplace(Result,'?',string(aWhereParams[i].VChar),[]);
  489. vtPChar : Result := StringReplace(Result,'?',string(aWhereParams[i].VPChar),[]);
  490. else Result := StringReplace(Result,'?', DbQuotedStr(string(aWhereParams[i].VUnicodeString)),[]);
  491. end;
  492. end;
  493. end;
  494. function TLinqQuery<T>.Where(const aWhereClause: string; aWhereParams: array of const): ILinqQuery<T>;
  495. begin
  496. Result := Where(FormatParams(aWhereClause,aWhereParams));
  497. end;
  498. function TLinqQuery<T>.Where(const aWhereClause: string): ILinqQuery<T>;
  499. begin
  500. Result := Self;
  501. try
  502. fWhereClause := TExpressionParser.Parse(aWhereClause);
  503. except
  504. on E : Exception do raise ELinqNotValidExpression.Create(e.Message);
  505. end;
  506. end;
  507. {$IFNDEF FPC}
  508. function TLinqQuery<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
  509. begin
  510. Result := Self;
  511. fWhereClause := TLinqExpression<T>.Create(aPredicate);
  512. end;
  513. {$ENDIF}
  514. { TLinq }
  515. {$IFNDEF FPC}
  516. class function TLinq<T>.From(aObjectList: TObjectList<T>): ILinqQuery<T>;
  517. begin
  518. Result := TLinqQuery<T>.Create(aObjectList);
  519. end;
  520. {$ENDIF}
  521. class function TLinq<T>.From(aArray: TArray<T>): ILinqQuery<T>;
  522. begin
  523. Result := TLinqQuery<T>.Create(aArray);
  524. end;
  525. class function TLinq<T>.From(aXArray : TXArray<T>) : ILinqQuery<T>;
  526. begin
  527. Result := TLinqQuery<T>.Create(aXArray);
  528. end;
  529. { TLinqExpression<T> }
  530. {$IFNDEF FPC}
  531. constructor TLinqExpression<T>.Create(aPredicate: TPredicate<T>);
  532. begin
  533. fPredicate := aPredicate;
  534. end;
  535. function TLinqExpression<T>.Validate(const aValue : TValue) : Boolean;
  536. begin
  537. Result := fPredicate(aValue.AsType<T>);
  538. end;
  539. function TLinqExpression<T>.IsNull : Boolean;
  540. begin
  541. Result := not Assigned(fPredicate);
  542. end;
  543. { TLinqArray<T> }
  544. function TLinqArray<T>.Any : Boolean;
  545. begin
  546. Result := High(fArray) >= 0;
  547. end;
  548. function TLinqArray<T>.Any(const aMatchString: string; aUseRegEx: Boolean): Boolean;
  549. begin
  550. fMatchString := aMatchString;
  551. fUseRegEx := aUseRegEx;
  552. end;
  553. function TLinqArray<T>.Count: Integer;
  554. begin
  555. Result := High(fArray) + 1;
  556. end;
  557. constructor TLinqArray<T>.Create(aArray: TArray<T>);
  558. begin
  559. {$IFDEF DELPHIRX104_UP}
  560. Pointer(fArray) := aArray;
  561. {$ELSE}
  562. fArray := aArray;
  563. {$ENDIF}
  564. end;
  565. function TLinqArray<T>.Delete: Integer;
  566. var
  567. i : Integer;
  568. {$IFNDEF DELPHIXE7_UP}
  569. n : Integer;
  570. len : Integer;
  571. {$ENDIF}
  572. begin
  573. Result := 0;
  574. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  575. for i := High(fArray) downto Low(fArray) do
  576. begin
  577. if Validate(fArray[i]) then
  578. begin
  579. //TObject(fArray[i]).Free;
  580. {$IFDEF DELPHIXE7_UP}
  581. System.Delete(fArray,i,1);
  582. {$ELSE}
  583. len := Length(fArray);
  584. if (len > 0) and (i < len) then
  585. begin
  586. for n := i + 1 to len - 1 do fArray[n - 1] := fArray[n];
  587. SetLength(fArray, len - 1);
  588. end;
  589. {$ENDIF}
  590. Inc(Result);
  591. end;
  592. end;
  593. end;
  594. function TLinqArray<T>.OrderAscending: ILinqArray<T>;
  595. var
  596. comparer : IComparer<T>;
  597. begin
  598. comparer := TLinqComparer.Create(True);
  599. TArray.Sort<T>(fArray,comparer);
  600. end;
  601. function TLinqArray<T>.OrderDescending: ILinqArray<T>;
  602. var
  603. comparer : IComparer<T>;
  604. begin
  605. comparer := TLinqComparer.Create(False);
  606. TArray.Sort<T>(fArray,comparer);
  607. end;
  608. function TLinqArray<T>.Select: TArray<T>;
  609. var
  610. value : T;
  611. begin
  612. Result := [];
  613. //DoOrderBy(fList);
  614. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  615. for value in fArray do
  616. begin
  617. if Validate(value) then Result := Result + [value];
  618. end;
  619. end;
  620. function TLinqArray<T>.SelectFirst: T;
  621. var
  622. value : T;
  623. begin
  624. //DoOrderBy(fList);
  625. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  626. for value in fArray do
  627. begin
  628. if Validate(value) then Exit(value);
  629. end;
  630. end;
  631. function TLinqArray<T>.SelectLast: T;
  632. var
  633. value : T;
  634. found : T;
  635. begin
  636. //DoOrderBy(fList);
  637. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  638. for value in fArray do
  639. begin
  640. if Validate(value) then found := value;
  641. end;
  642. Result := found;
  643. end;
  644. function TLinqArray<T>.SelectTop(aLimit: Integer): TArray<T>;
  645. var
  646. i : Integer;
  647. limit : Integer;
  648. begin
  649. Result := [];
  650. if aLimit > High(fArray) then limit := High(fArray)
  651. else limit := aLimit;
  652. SetLength(Result,limit);
  653. for i := Low(fArray) to limit do
  654. begin
  655. Result[i] := fArray[i];
  656. end;
  657. end;
  658. function TLinqArray<T>.Update(const aNewValue: T): Integer;
  659. var
  660. i : Integer;
  661. begin
  662. for i := Low(fArray) to High(fArray) do
  663. begin
  664. if Validate(fArray[i]) then fArray[i] := aNewValue;
  665. end;
  666. end;
  667. function TLinqArray<T>.Validate(aValue: T): Boolean;
  668. var
  669. regEx : TRegEx;
  670. value : TValue;
  671. begin
  672. value := TValue.From<T>(aValue);
  673. if fUseRegEx then
  674. begin
  675. regEx := TRegEx.Create(fMatchString,[roIgnoreCase,roMultiline]);
  676. try
  677. Result := regEx.IsMatch(value.AsString);
  678. except
  679. raise Exception.Create('TLinqArray not valid RegEx!');
  680. end;
  681. end
  682. else
  683. begin
  684. Result := CompareText(fMatchString,value.AsString) = 0;
  685. end;
  686. end;
  687. function TLinqArray<T>.Where(const aMatchString: string; aUseRegEx: Boolean): ILinqArray<T>;
  688. begin
  689. Result := Self;
  690. fMatchString := aMatchString;
  691. fUseRegEx := aUseRegEx;
  692. end;
  693. { TLinqArray<T>.TLinqComparer }
  694. constructor TLinqArray<T>.TLinqComparer.Create(aSortAscending : Boolean);
  695. begin
  696. fSortAscending := aSortAscending;
  697. end;
  698. function TLinqArray<T>.TLinqComparer.Compare(const L, R: T): Integer;
  699. var
  700. valueL : TValue;
  701. valueR : TValue;
  702. hr : Integer;
  703. lr : Integer;
  704. begin
  705. Result := 0;
  706. if fSortAscending then
  707. begin
  708. hr := 1;
  709. lr := -1;
  710. end
  711. else
  712. begin
  713. hr := -1;
  714. lr := 1;
  715. end;
  716. valueL := TValue.From<T>(L);
  717. valueR := TValue.From<T>(R);
  718. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(hr)
  719. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(lr);
  720. case valueL.Kind of
  721. {$IFNDEF FPC}
  722. tkString,
  723. {$ENDIF}
  724. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  725. tkInteger, tkInt64 :
  726. begin
  727. if valueL.AsInteger > valueR.AsInteger then Result := hr
  728. else if valueL.AsInteger < valueR.AsInteger then Result := lr;
  729. end;
  730. tkFloat :
  731. begin
  732. if valueL.AsExtended > valueR.AsExtended then Result := hr
  733. else if valueL.AsExtended < valueR.AsExtended then Result := lr;
  734. end;
  735. end;
  736. end;
  737. { TLinqArrayHelper }
  738. function TLinqArrayHelper.Add(const aValue : string) : Integer;
  739. begin
  740. SetLength(Self,Length(Self)+1);
  741. Self[High(Self)] := aValue;
  742. Result := High(Self);
  743. end;
  744. function TLinqArrayHelper.AddIfNotExists(const aValue : string) : Integer;
  745. var
  746. i : Integer;
  747. begin
  748. for i := Low(Self) to High(Self) do
  749. begin
  750. if CompareText(Self[i],aValue) = 0 then Exit(i);
  751. end;
  752. //if not exists add it
  753. Result := Self.Add(aValue);
  754. end;
  755. function TLinqArrayHelper.Remove(const aValue : string) : Boolean;
  756. var
  757. i : Integer;
  758. begin
  759. for i := Low(Self) to High(Self) do
  760. begin
  761. if CompareText(Self[i],aValue) = 0 then
  762. begin
  763. System.Delete(Self,i,1);
  764. Exit(True);
  765. end;
  766. end;
  767. Result := False;
  768. end;
  769. function TLinqArrayHelper.Any : Boolean;
  770. begin
  771. Result := High(Self) >= 0;
  772. end;
  773. function TLinqArrayHelper.Any(const aValue : string) : Boolean;
  774. var
  775. value : string;
  776. begin
  777. Result := False;
  778. for value in Self do
  779. begin
  780. if CompareText(value,aValue) = 0 then Exit(True)
  781. end;
  782. end;
  783. function TLinqArrayHelper.Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean;
  784. begin
  785. Result := TLinqArray<string>.Create(Self).Any(aMatchString,aUseRegEx);
  786. end;
  787. function TLinqArrayHelper.Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>;
  788. begin
  789. Result := TLinqArray<string>.Create(Self).Where(aMatchString,aUseRegEx);
  790. end;
  791. {$ENDIF}
  792. end.