Quick.Linq.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. { ***************************************************************************
  2. Copyright (c) 2016-2019 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 : 12/03/20120
  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. TLinqArray<T> = class(TInterfacedObject,ILinqArray<T>)
  67. private
  68. fArray : TArray<T>;
  69. fMatchString : string;
  70. fUseRegEx : Boolean;
  71. function Validate(aValue : T) : Boolean;
  72. public
  73. constructor Create(aArray : TArray<T>);
  74. function Any : Boolean; overload;
  75. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  76. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<T>; overload;
  77. function OrderAscending : ILinqArray<T>;
  78. function OrderDescending : ILinqArray<T>;
  79. function SelectFirst : T;
  80. function SelectLast : T;
  81. function SelectTop(aLimit : Integer) : TArray<T>;
  82. function Select : TArray<T>; overload;
  83. function Count : Integer;
  84. function Update(const aNewValue : T) : Integer;
  85. function Delete : Integer;
  86. end;
  87. {$IFNDEF FPC}
  88. TLinqArrayHelper = record helper for TArray<string>
  89. function Add(const aValue : string) : Integer;
  90. function AddIfNotExists(const aValue : string) : Integer;
  91. function Remove(const aValue : string) : Boolean;
  92. function Any : Boolean; overload;
  93. function Any(const aValue : string) : Boolean; overload;
  94. function Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean; overload;
  95. function Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>; overload;
  96. end;
  97. {$ENDIF}
  98. ILinqQuery<T> = interface
  99. ['{16B68C0B-EA38-488A-99D9-BAD1E8560E8E}']
  100. function Where(const aWhereClause : string; aWhereValues : array of const) : ILinqQuery<T>; overload;
  101. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  102. {$IFNDEF FPC}
  103. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  104. {$ENDIF}
  105. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  106. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  107. function SelectFirst : T;
  108. function SelectLast : T;
  109. function SelectTop(aLimit : Integer) : TxArray<T>;
  110. function Select : TxArray<T>; overload;
  111. function Select(const aPropertyName : string) : TFlexArray; overload;
  112. function Count : Integer;
  113. function Update(const aFields : array of string; aValues : array of const) : Integer;
  114. function Delete : Integer;
  115. end;
  116. TLinqQuery<T : class> = class(TInterfacedObject,ILinqQuery<T>)
  117. private type
  118. arrayOfT = array of T;
  119. private
  120. fWhereClause : TExpression;
  121. fOrderBy : TArray<string>;
  122. fOrderDirection : TOrderDirection;
  123. //fPList : Pointer;
  124. fList : arrayOfT;
  125. function FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  126. procedure DoOrderBy(vArray : ArrayOfT);
  127. function Compare(const aPropertyName : string; L, R : T) : Integer;
  128. procedure Clear;
  129. public
  130. {$IFNDEF FPC}
  131. constructor Create(aObjectList : TObjectList<T>); overload;
  132. {$ENDIF}
  133. constructor Create(aList : TList<T>); overload;
  134. constructor Create(aXArray : TxArray<T>); overload;
  135. constructor Create(aArray : TArray<T>); overload;
  136. destructor Destroy; override;
  137. function Where(const aWhereClause : string; aWhereParams : array of const) : ILinqQuery<T>; overload;
  138. function Where(const aWhereClause: string): ILinqQuery<T>; overload;
  139. {$IFNDEF FPC}
  140. function Where(aPredicate : TPredicate<T>) : ILinqQuery<T>; overload;
  141. {$ENDIF}
  142. function OrderBy(const aFieldNames : string) : ILinqQuery<T>;
  143. function OrderByDescending(const aFieldNames : string) : ILinqQuery<T>;
  144. function SelectFirst : T;
  145. function SelectLast : T;
  146. function SelectTop(aLimit : Integer) : TxArray<T>;
  147. function Select : TxArray<T>; overload;
  148. function Select(const aPropertyName : string) : TFlexArray; overload;
  149. function Count : Integer;
  150. function Update(const aFields : array of string; aValues : array of const) : Integer;
  151. function Delete : Integer;
  152. end;
  153. TLinq<T : class> = class
  154. public
  155. {$IFNDEF FPC}
  156. class function From(aObjectList : TObjectList<T>) : ILinqQuery<T>; overload;
  157. {$ENDIF}
  158. class function From(aArray : TArray<T>) : ILinqQuery<T>; overload;
  159. class function From(aXArray : TXArray<T>) : ILinqQuery<T>; overload;
  160. end;
  161. ELinqNotValidExpression = class(Exception);
  162. ELinqError = class(Exception);
  163. implementation
  164. { TLinqQuery<T> }
  165. procedure TLinqQuery<T>.Clear;
  166. begin
  167. SetLength(fOrderBy,0);
  168. end;
  169. constructor TLinqQuery<T>.Create(aArray: TArray<T>);
  170. begin
  171. Clear;
  172. fList := aArray;
  173. end;
  174. {$IFNDEF FPC}
  175. constructor TLinqQuery<T>.Create(aObjectList: TObjectList<T>);
  176. begin
  177. Clear;
  178. //Create(aObjectList.List);
  179. //fPList := Pointer(aObjectList.List);
  180. //fList := arrayOfT(fPList);
  181. fList := aObjectList.List;
  182. end;
  183. {$ENDIF}
  184. constructor TLinqQuery<T>.Create(aXArray: TxArray<T>);
  185. begin
  186. Clear;
  187. fList := aXArray;
  188. end;
  189. constructor TLinqQuery<T>.Create(aList: TList<T>);
  190. begin
  191. Clear;
  192. fList := aList.ToArray;
  193. end;
  194. function TLinqQuery<T>.Compare(const aPropertyName: string; L, R: T): Integer;
  195. var
  196. valueL : TValue;
  197. valueR : TValue;
  198. begin
  199. Result := 0;
  200. valueL := TRTTI.GetPathValue(L,aPropertyName);
  201. valueR := TRTTI.GetPathValue(R,aPropertyName);
  202. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(1)
  203. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(-1);
  204. case valueL.Kind of
  205. {$IFNDEF FPC}
  206. tkString,
  207. {$ENDIF}
  208. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  209. tkInteger, tkInt64 :
  210. begin
  211. if valueL.AsInteger > valueR.AsInteger then Result := 1
  212. else if valueL.AsInteger < valueR.AsInteger then Result := -1;
  213. end;
  214. tkFloat :
  215. begin
  216. if valueL.AsExtended > valueR.AsExtended then Result := 1
  217. else if valueL.AsExtended < valueR.AsExtended then Result := -1;
  218. end;
  219. end;
  220. end;
  221. function TLinqQuery<T>.Count: Integer;
  222. var
  223. i : Integer;
  224. begin
  225. Result := 0;
  226. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  227. for i := High(fList) downto Low(fList) do
  228. begin
  229. if fWhereClause.Validate(fList[i].AsVariant) then
  230. begin
  231. Inc(Result);
  232. end;
  233. end;
  234. end;
  235. function TLinqQuery<T>.Delete: Integer;
  236. var
  237. i : Integer;
  238. begin
  239. Result := 0;
  240. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  241. for i := High(fList) downto Low(fList) do
  242. begin
  243. if fWhereClause.Validate(fList[i]) then
  244. begin
  245. TObject(fList[i]).Free;
  246. //System.Delete(fList,i,1);
  247. Inc(Result);
  248. end;
  249. end;
  250. end;
  251. destructor TLinqQuery<T>.Destroy;
  252. begin
  253. if Assigned(fWhereClause) then fWhereClause.Free;
  254. inherited;
  255. end;
  256. procedure TLinqQuery<T>.DoOrderBy(vArray : ArrayOfT);
  257. begin
  258. if High(fOrderBy) < 0 then Exit;
  259. {$IFNDEF FPC}
  260. TArray.Sort<T>(vArray, TComparer<T>.Construct(
  261. function (const A, B: T): Integer
  262. var
  263. field : string;
  264. begin
  265. for field in fOrderBy do
  266. begin
  267. Result := Compare(field,A,B);
  268. if Result <> 0 then Break;
  269. end;
  270. if fOrderDirection = TOrderDirection.odDescending then Result := Result * -1;
  271. end)
  272. );
  273. {$ENDIF}
  274. end;
  275. function TLinqQuery<T>.OrderBy(const aFieldNames: string): ILinqQuery<T>;
  276. begin
  277. Result := Self;
  278. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  279. fOrderBy := aFieldNames.Split([',']);
  280. fOrderDirection := TOrderDirection.odAscending;
  281. end;
  282. function TLinqQuery<T>.OrderByDescending(const aFieldNames: string): ILinqQuery<T>;
  283. begin
  284. Result := Self;
  285. if aFieldNames = '' then raise ELinqError.Create('No order fields specified!');
  286. fOrderBy := aFieldNames.Split([',']);
  287. fOrderDirection := TOrderDirection.odDescending;
  288. end;
  289. function TLinqQuery<T>.Select(const aPropertyName: string): TFlexArray;
  290. var
  291. obj : T;
  292. value : TFlexValue;
  293. begin
  294. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  295. for obj in fList do
  296. begin
  297. if fWhereClause.Validate(obj) then
  298. begin
  299. //value := TRTTI.GetProperty(obj,aPropertyName);
  300. {$IFNDEF FPC}
  301. value := TRTTI.GetPathValue(obj,aPropertyName).AsVariant;
  302. {$ELSE}
  303. value.AsTValue := TRTTI.GetPathValue(obj,aPropertyName);
  304. {$ENDIF}
  305. Result.Add(value);
  306. end;
  307. end;
  308. end;
  309. function TLinqQuery<T>.Select: TxArray<T>;
  310. var
  311. obj : T;
  312. begin
  313. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  314. for obj in fList do
  315. begin
  316. if fWhereClause.Validate(obj) then Result.Add(obj);
  317. end;
  318. DoOrderBy(Result);
  319. end;
  320. function TLinqQuery<T>.SelectFirst: T;
  321. var
  322. obj : T;
  323. begin
  324. {$IFNDEF FPC}
  325. Result := nil;
  326. {$ENDIF}
  327. DoOrderBy(fList);
  328. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  329. for obj in fList do
  330. begin
  331. if fWhereClause.Validate(obj) then Exit(obj);
  332. end;
  333. end;
  334. function TLinqQuery<T>.SelectLast: T;
  335. var
  336. obj : T;
  337. begin
  338. {$IFNDEF FPC}
  339. Result := nil;
  340. {$ENDIF}
  341. DoOrderBy(fList);
  342. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  343. for obj in fList do
  344. begin
  345. if fWhereClause.Validate(obj) then Result := obj;
  346. end;
  347. end;
  348. function TLinqQuery<T>.SelectTop(aLimit: Integer): TxArray<T>;
  349. var
  350. obj : T;
  351. i : Integer;
  352. begin
  353. DoOrderBy(fList);
  354. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  355. i := 0;
  356. for obj in fList do
  357. begin
  358. if fWhereClause.Validate(obj) then
  359. begin
  360. Result.Add(obj);
  361. Inc(i);
  362. if i > aLimit then Exit;
  363. end;
  364. end;
  365. end;
  366. function TLinqQuery<T>.Update(const aFields: array of string; aValues: array of const): Integer;
  367. var
  368. obj : T;
  369. i : Integer;
  370. {$IFDEF FPC}
  371. value : TValue;
  372. {$ENDIF}
  373. begin
  374. Result := 0;
  375. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  376. for obj in fList do
  377. begin
  378. {$IFNDEF FPC}
  379. if obj = nil then continue;
  380. {$ELSE}
  381. if Pointer(obj) = nil then continue;
  382. {$ENDIF}
  383. if fWhereClause.Validate(obj) then
  384. begin
  385. for i := Low(aFields) to High(aFields) do
  386. begin
  387. if not TRTTI.PropertyExists(TypeInfo(T),aFields[i]) then raise ELinqError.CreateFmt('Linq update field "%s" not found in obj',[aFields[i]]);
  388. try
  389. {$IFNDEF FPC}
  390. TRTTI.SetPropertyValue(obj,aFields[i],aValues[i]);
  391. {$ELSE}
  392. case aValues[i].VType of
  393. vtString : value := string(aValues[i].VString^);
  394. vtChar : value := string(aValues[i].VChar);
  395. {$IFDEF MSWINDOWS}
  396. vtAnsiString : value := AnsiString(aValues[i].VAnsiString);
  397. vtWideString : value := WideString(aValues[i].VWideString);
  398. {$ENDIF}
  399. {$IFDEF UNICODE}
  400. vtUnicodeString: AsString := string(aValues[i].VUnicodeString);
  401. {$ENDIF UNICODE}
  402. vtInteger : value := aValues[i].VInteger;
  403. vtInt64 : value := aValues[i].VInt64^;
  404. vtExtended : value := aValues[i].VExtended^;
  405. vtBoolean : value := aValues[i].VBoolean;
  406. else raise Exception.Create('DataType not supported by Linq update');
  407. end;
  408. TRTTI.SetPropertyValue(obj,aFields[i],value);
  409. {$ENDIF}
  410. except
  411. on E : Exception do raise ELinqError.CreateFmt('Linq update error: %s',[e.Message]);
  412. end;
  413. end;
  414. Inc(Result);
  415. end;
  416. end;
  417. end;
  418. function TLinqQuery<T>.FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  419. var
  420. i : Integer;
  421. begin
  422. Result := aWhereClause;
  423. if aWhereClause = '' then
  424. begin
  425. Result := '1 = 1';
  426. Exit;
  427. end;
  428. for i := 0 to aWhereClause.CountChar('?') - 1 do
  429. begin
  430. case aWhereParams[i].VType of
  431. vtInteger : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInteger),[]);
  432. vtInt64 : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInt64^),[]);
  433. vtExtended : Result := StringReplace(Result,'?',FloatToStr(aWhereParams[i].VExtended^),[]);
  434. vtBoolean : Result := StringReplace(Result,'?',BoolToStr(aWhereParams[i].VBoolean),[]);
  435. vtAnsiString : Result := StringReplace(Result,'?',string(aWhereParams[i].VAnsiString),[]);
  436. vtWideString : Result := StringReplace(Result,'?',string(aWhereParams[i].VWideString^),[]);
  437. {$IFNDEF NEXTGEN}
  438. vtString : Result := StringReplace(Result,'?',aWhereParams[i].VString^,[]);
  439. {$ENDIF}
  440. vtChar : Result := StringReplace(Result,'?',aWhereParams[i].VChar,[]);
  441. vtPChar : Result := StringReplace(Result,'?',string(aWhereParams[i].VPChar),[]);
  442. else Result := StringReplace(Result,'?', DbQuotedStr(string(aWhereParams[i].VUnicodeString)),[]);
  443. end;
  444. end;
  445. end;
  446. function TLinqQuery<T>.Where(const aWhereClause: string; aWhereParams: array of const): ILinqQuery<T>;
  447. begin
  448. Result := Where(FormatParams(aWhereClause,aWhereParams));
  449. end;
  450. function TLinqQuery<T>.Where(const aWhereClause: string): ILinqQuery<T>;
  451. begin
  452. Result := Self;
  453. try
  454. fWhereClause := TExpressionParser.Parse(aWhereClause);
  455. except
  456. on E : Exception do raise ELinqNotValidExpression.Create(e.Message);
  457. end;
  458. end;
  459. {$IFNDEF FPC}
  460. function TLinqQuery<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
  461. begin
  462. Result := Self;
  463. fWhereClause := TLinqExpression<T>.Create(aPredicate);
  464. end;
  465. {$ENDIF}
  466. { TLinq }
  467. {$IFNDEF FPC}
  468. class function TLinq<T>.From(aObjectList: TObjectList<T>): ILinqQuery<T>;
  469. begin
  470. Result := TLinqQuery<T>.Create(aObjectList);
  471. end;
  472. {$ENDIF}
  473. class function TLinq<T>.From(aArray: TArray<T>): ILinqQuery<T>;
  474. begin
  475. Result := TLinqQuery<T>.Create(aArray);
  476. end;
  477. class function TLinq<T>.From(aXArray : TXArray<T>) : ILinqQuery<T>;
  478. begin
  479. Result := TLinqQuery<T>.Create(aXArray);
  480. end;
  481. { TLinqExpression<T> }
  482. {$IFNDEF FPC}
  483. constructor TLinqExpression<T>.Create(aPredicate: TPredicate<T>);
  484. begin
  485. fPredicate := aPredicate;
  486. end;
  487. function TLinqExpression<T>.Validate(const aValue : TValue) : Boolean;
  488. begin
  489. Result := fPredicate(aValue.AsType<T>);
  490. end;
  491. function TLinqExpression<T>.IsNull : Boolean;
  492. begin
  493. Result := not Assigned(fPredicate);
  494. end;
  495. {$ENDIF}
  496. { TLinqArray<T> }
  497. function TLinqArray<T>.Any : Boolean;
  498. begin
  499. Result := High(fArray) >= 0;
  500. end;
  501. function TLinqArray<T>.Any(const aMatchString: string; aUseRegEx: Boolean): Boolean;
  502. begin
  503. fMatchString := aMatchString;
  504. fUseRegEx := aUseRegEx;
  505. end;
  506. function TLinqArray<T>.Count: Integer;
  507. begin
  508. Result := High(fArray) + 1;
  509. end;
  510. constructor TLinqArray<T>.Create(aArray: TArray<T>);
  511. begin
  512. fArray := aArray;
  513. end;
  514. function TLinqArray<T>.Delete: Integer;
  515. var
  516. i : Integer;
  517. begin
  518. Result := 0;
  519. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  520. for i := High(fArray) downto Low(fArray) do
  521. begin
  522. if Validate(fArray[i]) then
  523. begin
  524. //TObject(fArray[i]).Free;
  525. System.Delete(fArray,i,1);
  526. Inc(Result);
  527. end;
  528. end;
  529. end;
  530. function TLinqArray<T>.OrderAscending: ILinqArray<T>;
  531. begin
  532. end;
  533. function TLinqArray<T>.OrderDescending: ILinqArray<T>;
  534. begin
  535. end;
  536. function TLinqArray<T>.Select: TArray<T>;
  537. var
  538. value : T;
  539. begin
  540. //DoOrderBy(fList);
  541. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  542. for value in fArray do
  543. begin
  544. if Validate(value) then Result := Result + [value];
  545. end;
  546. end;
  547. function TLinqArray<T>.SelectFirst: T;
  548. var
  549. value : T;
  550. begin
  551. //DoOrderBy(fList);
  552. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  553. for value in fArray do
  554. begin
  555. if Validate(value) then Exit(value);
  556. end;
  557. end;
  558. function TLinqArray<T>.SelectLast: T;
  559. var
  560. value : T;
  561. found : T;
  562. begin
  563. //DoOrderBy(fList);
  564. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  565. for value in fArray do
  566. begin
  567. if Validate(value) then found := value;
  568. end;
  569. Result := found;
  570. end;
  571. function TLinqArray<T>.SelectTop(aLimit: Integer): TArray<T>;
  572. var
  573. i : Integer;
  574. limit : Integer;
  575. begin
  576. if aLimit > High(fArray) then limit := High(fArray)
  577. else limit := aLimit;
  578. SetLength(Result,limit);
  579. for i := Low(fArray) to limit do
  580. begin
  581. Result[i] := fArray[i];
  582. end;
  583. end;
  584. function TLinqArray<T>.Update(const aNewValue: T): Integer;
  585. begin
  586. end;
  587. function TLinqArray<T>.Validate(aValue: T): Boolean;
  588. var
  589. regEx : TRegEx;
  590. value : TValue;
  591. begin
  592. value := TValue.From<T>(aValue);
  593. if fUseRegEx then
  594. begin
  595. regEx := TRegEx.Create(fMatchString,[roIgnoreCase,roMultiline]);
  596. try
  597. Result := regEx.IsMatch(value.AsString);
  598. except
  599. raise Exception.Create('TLinqArray not valid RegEx!');
  600. end;
  601. end
  602. else
  603. begin
  604. Result := CompareText(fMatchString,value.AsString) = 0;
  605. end;
  606. end;
  607. function TLinqArray<T>.Where(const aMatchString: string; aUseRegEx: Boolean): ILinqArray<T>;
  608. begin
  609. Result := Self;
  610. fMatchString := aMatchString;
  611. fUseRegEx := aUseRegEx;
  612. end;
  613. { TLinqArrayHelper }
  614. {$IFNDEF FPC}
  615. function TLinqArrayHelper.Add(const aValue : string) : Integer;
  616. begin
  617. SetLength(Self,Length(Self)+1);
  618. Self[High(Self)] := aValue;
  619. Result := High(Self);
  620. end;
  621. function TLinqArrayHelper.AddIfNotExists(const aValue : string) : Integer;
  622. var
  623. i : Integer;
  624. begin
  625. for i := Low(Self) to High(Self) do
  626. begin
  627. if CompareText(Self[i],aValue) = 0 then Exit(i);
  628. end;
  629. //if not exists add it
  630. Result := Self.Add(aValue);
  631. end;
  632. function TLinqArrayHelper.Remove(const aValue : string) : Boolean;
  633. var
  634. i : Integer;
  635. begin
  636. for i := Low(Self) to High(Self) do
  637. begin
  638. if CompareText(Self[i],aValue) = 0 then
  639. begin
  640. System.Delete(Self,i,1);
  641. Exit(True);
  642. end;
  643. end;
  644. Result := False;
  645. end;
  646. function TLinqArrayHelper.Any : Boolean;
  647. begin
  648. Result := High(Self) >= 0;
  649. end;
  650. function TLinqArrayHelper.Any(const aValue : string) : Boolean;
  651. var
  652. value : string;
  653. begin
  654. Result := False;
  655. for value in Self do
  656. begin
  657. if CompareText(value,aValue) = 0 then Exit(True)
  658. end;
  659. end;
  660. function TLinqArrayHelper.Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean;
  661. begin
  662. Result := TLinqArray<string>.Create(Self).Any(aMatchString,aUseRegEx);
  663. end;
  664. function TLinqArrayHelper.Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>;
  665. begin
  666. Result := TLinqArray<string>.Create(Self).Where(aMatchString,aUseRegEx);
  667. end;
  668. {$ENDIF}
  669. end.