Quick.Linq.pas 22 KB

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