2
0

Quick.Linq.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  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. {$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. 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 Defined(FPC) OR Defined(DELPHIRX102_UP)}
  323. Result := [];
  324. {$ELSE}
  325. Result := nil;
  326. {$ENDIF}
  327. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  328. for obj in fList do
  329. begin
  330. if fWhereClause.Validate(obj) then Result.Add(obj);
  331. end;
  332. DoOrderBy(Result);
  333. end;
  334. function TLinqQuery<T>.SelectFirst: 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 Exit(obj);
  346. end;
  347. end;
  348. function TLinqQuery<T>.SelectLast: T;
  349. var
  350. obj : T;
  351. begin
  352. {$IFNDEF FPC}
  353. Result := nil;
  354. {$ENDIF}
  355. DoOrderBy(fList);
  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 := obj;
  360. end;
  361. end;
  362. function TLinqQuery<T>.SelectTop(aLimit: Integer): TxArray<T>;
  363. var
  364. obj : T;
  365. i : Integer;
  366. begin
  367. {$If Defined(FPC) OR Defined(DELPHIRX102_UP)}
  368. Result := [];
  369. {$ELSE}
  370. Result := nil;
  371. {$ENDIF}
  372. DoOrderBy(fList);
  373. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  374. i := 0;
  375. for obj in fList do
  376. begin
  377. if fWhereClause.Validate(obj) then
  378. begin
  379. Result.Add(obj);
  380. Inc(i);
  381. if i > aLimit then Exit;
  382. end;
  383. end;
  384. end;
  385. function TLinqQuery<T>.Update(const aFields: array of string; aValues: array of const): Integer;
  386. var
  387. obj : T;
  388. i : Integer;
  389. {$IFDEF FPC}
  390. value : TValue;
  391. {$ENDIF}
  392. begin
  393. Result := 0;
  394. if fWhereClause = nil then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  395. for obj in fList do
  396. begin
  397. {$IFNDEF FPC}
  398. if obj = nil then continue;
  399. {$ELSE}
  400. if Pointer(obj) = nil then continue;
  401. {$ENDIF}
  402. if fWhereClause.Validate(obj) then
  403. begin
  404. for i := Low(aFields) to High(aFields) do
  405. begin
  406. if not TRTTI.PropertyExists(TypeInfo(T),aFields[i]) then raise ELinqError.CreateFmt('Linq update field "%s" not found in obj',[aFields[i]]);
  407. try
  408. {$IFNDEF FPC}
  409. TRTTI.SetPropertyValue(obj,aFields[i],aValues[i]);
  410. {$ELSE}
  411. case aValues[i].VType of
  412. vtString : value := string(aValues[i].VString^);
  413. vtChar : value := string(aValues[i].VChar);
  414. {$IFDEF MSWINDOWS}
  415. vtAnsiString : value := AnsiString(aValues[i].VAnsiString);
  416. vtWideString : value := WideString(aValues[i].VWideString);
  417. {$ENDIF}
  418. {$IFDEF UNICODE}
  419. vtUnicodeString: AsString := string(aValues[i].VUnicodeString);
  420. {$ENDIF UNICODE}
  421. vtInteger : value := aValues[i].VInteger;
  422. vtInt64 : value := aValues[i].VInt64^;
  423. vtExtended : value := aValues[i].VExtended^;
  424. vtBoolean : value := aValues[i].VBoolean;
  425. else raise Exception.Create('DataType not supported by Linq update');
  426. end;
  427. TRTTI.SetPropertyValue(obj,aFields[i],value);
  428. {$ENDIF}
  429. except
  430. on E : Exception do raise ELinqError.CreateFmt('Linq update error: %s',[e.Message]);
  431. end;
  432. end;
  433. Inc(Result);
  434. end;
  435. end;
  436. end;
  437. function TLinqQuery<T>.FormatParams(const aWhereClause : string; aWhereParams : array of const) : string;
  438. var
  439. i : Integer;
  440. begin
  441. Result := aWhereClause;
  442. if aWhereClause = '' then
  443. begin
  444. Result := '1 = 1';
  445. Exit;
  446. end;
  447. for i := 0 to aWhereClause.CountChar('?') - 1 do
  448. begin
  449. case aWhereParams[i].VType of
  450. vtInteger : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInteger),[]);
  451. vtInt64 : Result := StringReplace(Result,'?',IntToStr(aWhereParams[i].VInt64^),[]);
  452. vtExtended : Result := StringReplace(Result,'?',FloatToStr(aWhereParams[i].VExtended^),[]);
  453. vtBoolean : Result := StringReplace(Result,'?',BoolToStr(aWhereParams[i].VBoolean),[]);
  454. vtAnsiString : Result := StringReplace(Result,'?',string(aWhereParams[i].VAnsiString),[]);
  455. vtWideString : Result := StringReplace(Result,'?',string(aWhereParams[i].VWideString^),[]);
  456. {$IFNDEF NEXTGEN}
  457. vtString : Result := StringReplace(Result,'?',aWhereParams[i].VString^,[]);
  458. {$ENDIF}
  459. vtChar : Result := StringReplace(Result,'?',aWhereParams[i].VChar,[]);
  460. vtPChar : Result := StringReplace(Result,'?',string(aWhereParams[i].VPChar),[]);
  461. else Result := StringReplace(Result,'?', DbQuotedStr(string(aWhereParams[i].VUnicodeString)),[]);
  462. end;
  463. end;
  464. end;
  465. function TLinqQuery<T>.Where(const aWhereClause: string; aWhereParams: array of const): ILinqQuery<T>;
  466. begin
  467. Result := Where(FormatParams(aWhereClause,aWhereParams));
  468. end;
  469. function TLinqQuery<T>.Where(const aWhereClause: string): ILinqQuery<T>;
  470. begin
  471. Result := Self;
  472. try
  473. fWhereClause := TExpressionParser.Parse(aWhereClause);
  474. except
  475. on E : Exception do raise ELinqNotValidExpression.Create(e.Message);
  476. end;
  477. end;
  478. {$IFNDEF FPC}
  479. function TLinqQuery<T>.Where(aPredicate: TPredicate<T>): ILinqQuery<T>;
  480. begin
  481. Result := Self;
  482. fWhereClause := TLinqExpression<T>.Create(aPredicate);
  483. end;
  484. {$ENDIF}
  485. { TLinq }
  486. {$IFNDEF FPC}
  487. class function TLinq<T>.From(aObjectList: TObjectList<T>): ILinqQuery<T>;
  488. begin
  489. Result := TLinqQuery<T>.Create(aObjectList);
  490. end;
  491. {$ENDIF}
  492. class function TLinq<T>.From(aArray: TArray<T>): ILinqQuery<T>;
  493. begin
  494. Result := TLinqQuery<T>.Create(aArray);
  495. end;
  496. class function TLinq<T>.From(aXArray : TXArray<T>) : ILinqQuery<T>;
  497. begin
  498. Result := TLinqQuery<T>.Create(aXArray);
  499. end;
  500. { TLinqExpression<T> }
  501. {$IFNDEF FPC}
  502. constructor TLinqExpression<T>.Create(aPredicate: TPredicate<T>);
  503. begin
  504. fPredicate := aPredicate;
  505. end;
  506. function TLinqExpression<T>.Validate(const aValue : TValue) : Boolean;
  507. begin
  508. Result := fPredicate(aValue.AsType<T>);
  509. end;
  510. function TLinqExpression<T>.IsNull : Boolean;
  511. begin
  512. Result := not Assigned(fPredicate);
  513. end;
  514. { TLinqArray<T> }
  515. function TLinqArray<T>.Any : Boolean;
  516. begin
  517. Result := High(fArray) >= 0;
  518. end;
  519. function TLinqArray<T>.Any(const aMatchString: string; aUseRegEx: Boolean): Boolean;
  520. begin
  521. fMatchString := aMatchString;
  522. fUseRegEx := aUseRegEx;
  523. end;
  524. function TLinqArray<T>.Count: Integer;
  525. begin
  526. Result := High(fArray) + 1;
  527. end;
  528. constructor TLinqArray<T>.Create(aArray: TArray<T>);
  529. begin
  530. fArray := aArray;
  531. end;
  532. function TLinqArray<T>.Delete: Integer;
  533. var
  534. i : Integer;
  535. {$IFNDEF DELPHIXE7_UP}
  536. n : Integer;
  537. len : Integer;
  538. {$ENDIF}
  539. begin
  540. Result := 0;
  541. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  542. for i := High(fArray) downto Low(fArray) do
  543. begin
  544. if Validate(fArray[i]) then
  545. begin
  546. //TObject(fArray[i]).Free;
  547. {$IFDEF DELPHIXE7_UP}
  548. System.Delete(fArray,i,1);
  549. {$ELSE}
  550. len := Length(fArray);
  551. if (len > 0) and (i < len) then
  552. begin
  553. for n := i + 1 to len - 1 do fArray[n - 1] := fArray[n];
  554. SetLength(fArray, len - 1);
  555. end;
  556. {$ENDIF}
  557. Inc(Result);
  558. end;
  559. end;
  560. end;
  561. function TLinqArray<T>.OrderAscending: ILinqArray<T>;
  562. var
  563. comparer : IComparer<T>;
  564. begin
  565. comparer := TLinqComparer.Create(True);
  566. TArray.Sort<T>(fArray,comparer);
  567. end;
  568. function TLinqArray<T>.OrderDescending: ILinqArray<T>;
  569. var
  570. comparer : IComparer<T>;
  571. begin
  572. comparer := TLinqComparer.Create(False);
  573. TArray.Sort<T>(fArray,comparer);
  574. end;
  575. function TLinqArray<T>.Select: TArray<T>;
  576. var
  577. value : T;
  578. begin
  579. Result := [];
  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 Result := Result + [value];
  585. end;
  586. end;
  587. function TLinqArray<T>.SelectFirst: T;
  588. var
  589. value : T;
  590. begin
  591. //DoOrderBy(fList);
  592. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  593. for value in fArray do
  594. begin
  595. if Validate(value) then Exit(value);
  596. end;
  597. end;
  598. function TLinqArray<T>.SelectLast: T;
  599. var
  600. value : T;
  601. found : T;
  602. begin
  603. //DoOrderBy(fList);
  604. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  605. for value in fArray do
  606. begin
  607. if Validate(value) then found := value;
  608. end;
  609. Result := found;
  610. end;
  611. function TLinqArray<T>.SelectTop(aLimit: Integer): TArray<T>;
  612. var
  613. i : Integer;
  614. limit : Integer;
  615. begin
  616. Result := [];
  617. if aLimit > High(fArray) then limit := High(fArray)
  618. else limit := aLimit;
  619. SetLength(Result,limit);
  620. for i := Low(fArray) to limit do
  621. begin
  622. Result[i] := fArray[i];
  623. end;
  624. end;
  625. function TLinqArray<T>.Update(const aNewValue: T): Integer;
  626. var
  627. i : Integer;
  628. begin
  629. for i := Low(fArray) to High(fArray) do
  630. begin
  631. if Validate(fArray[i]) then fArray[i] := aNewValue;
  632. end;
  633. end;
  634. function TLinqArray<T>.Validate(aValue: T): Boolean;
  635. var
  636. regEx : TRegEx;
  637. value : TValue;
  638. begin
  639. value := TValue.From<T>(aValue);
  640. if fUseRegEx then
  641. begin
  642. regEx := TRegEx.Create(fMatchString,[roIgnoreCase,roMultiline]);
  643. try
  644. Result := regEx.IsMatch(value.AsString);
  645. except
  646. raise Exception.Create('TLinqArray not valid RegEx!');
  647. end;
  648. end
  649. else
  650. begin
  651. Result := CompareText(fMatchString,value.AsString) = 0;
  652. end;
  653. end;
  654. function TLinqArray<T>.Where(const aMatchString: string; aUseRegEx: Boolean): ILinqArray<T>;
  655. begin
  656. Result := Self;
  657. fMatchString := aMatchString;
  658. fUseRegEx := aUseRegEx;
  659. end;
  660. { TLinqArray<T>.TLinqComparer }
  661. constructor TLinqArray<T>.TLinqComparer.Create(aSortAscending : Boolean);
  662. begin
  663. fSortAscending := aSortAscending;
  664. end;
  665. function TLinqArray<T>.TLinqComparer.Compare(const L, R: T): Integer;
  666. var
  667. valueL : TValue;
  668. valueR : TValue;
  669. hr : Integer;
  670. lr : Integer;
  671. begin
  672. Result := 0;
  673. if fSortAscending then
  674. begin
  675. hr := 1;
  676. lr := -1;
  677. end
  678. else
  679. begin
  680. hr := -1;
  681. lr := 1;
  682. end;
  683. valueL := TValue.From<T>(L);
  684. valueR := TValue.From<T>(R);
  685. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(hr)
  686. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(lr);
  687. case valueL.Kind of
  688. {$IFNDEF FPC}
  689. tkString,
  690. {$ENDIF}
  691. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  692. tkInteger, tkInt64 :
  693. begin
  694. if valueL.AsInteger > valueR.AsInteger then Result := hr
  695. else if valueL.AsInteger < valueR.AsInteger then Result := lr;
  696. end;
  697. tkFloat :
  698. begin
  699. if valueL.AsExtended > valueR.AsExtended then Result := hr
  700. else if valueL.AsExtended < valueR.AsExtended then Result := lr;
  701. end;
  702. end;
  703. end;
  704. { TLinqArrayHelper }
  705. function TLinqArrayHelper.Add(const aValue : string) : Integer;
  706. begin
  707. SetLength(Self,Length(Self)+1);
  708. Self[High(Self)] := aValue;
  709. Result := High(Self);
  710. end;
  711. function TLinqArrayHelper.AddIfNotExists(const aValue : string) : Integer;
  712. var
  713. i : Integer;
  714. begin
  715. for i := Low(Self) to High(Self) do
  716. begin
  717. if CompareText(Self[i],aValue) = 0 then Exit(i);
  718. end;
  719. //if not exists add it
  720. Result := Self.Add(aValue);
  721. end;
  722. function TLinqArrayHelper.Remove(const aValue : string) : Boolean;
  723. var
  724. i : Integer;
  725. begin
  726. for i := Low(Self) to High(Self) do
  727. begin
  728. if CompareText(Self[i],aValue) = 0 then
  729. begin
  730. System.Delete(Self,i,1);
  731. Exit(True);
  732. end;
  733. end;
  734. Result := False;
  735. end;
  736. function TLinqArrayHelper.Any : Boolean;
  737. begin
  738. Result := High(Self) >= 0;
  739. end;
  740. function TLinqArrayHelper.Any(const aValue : string) : Boolean;
  741. var
  742. value : string;
  743. begin
  744. Result := False;
  745. for value in Self do
  746. begin
  747. if CompareText(value,aValue) = 0 then Exit(True)
  748. end;
  749. end;
  750. function TLinqArrayHelper.Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean;
  751. begin
  752. Result := TLinqArray<string>.Create(Self).Any(aMatchString,aUseRegEx);
  753. end;
  754. function TLinqArrayHelper.Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>;
  755. begin
  756. Result := TLinqArray<string>.Create(Self).Where(aMatchString,aUseRegEx);
  757. end;
  758. {$ENDIF}
  759. end.