Quick.Linq.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  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 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. { TLinqArray<T> }
  505. function TLinqArray<T>.Any : Boolean;
  506. begin
  507. Result := High(fArray) >= 0;
  508. end;
  509. function TLinqArray<T>.Any(const aMatchString: string; aUseRegEx: Boolean): Boolean;
  510. begin
  511. fMatchString := aMatchString;
  512. fUseRegEx := aUseRegEx;
  513. end;
  514. function TLinqArray<T>.Count: Integer;
  515. begin
  516. Result := High(fArray) + 1;
  517. end;
  518. constructor TLinqArray<T>.Create(aArray: TArray<T>);
  519. begin
  520. fArray := aArray;
  521. end;
  522. function TLinqArray<T>.Delete: Integer;
  523. var
  524. i : Integer;
  525. {$IFNDEF DELPHIXE7_UP}
  526. n : Integer;
  527. len : Integer;
  528. {$ENDIF}
  529. begin
  530. Result := 0;
  531. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  532. for i := High(fArray) downto Low(fArray) do
  533. begin
  534. if Validate(fArray[i]) then
  535. begin
  536. //TObject(fArray[i]).Free;
  537. {$IFDEF DELPHIXE7_UP}
  538. System.Delete(fArray,i,1);
  539. {$ELSE}
  540. len := Length(fArray);
  541. if (len > 0) and (i < len) then
  542. begin
  543. for n := i + 1 to len - 1 do fArray[n - 1] := fArray[n];
  544. SetLength(fArray, len - 1);
  545. end;
  546. {$ENDIF}
  547. Inc(Result);
  548. end;
  549. end;
  550. end;
  551. function TLinqArray<T>.OrderAscending: ILinqArray<T>;
  552. var
  553. comparer : IComparer<T>;
  554. begin
  555. comparer := TLinqComparer.Create(True);
  556. TArray.Sort<T>(fArray,comparer);
  557. end;
  558. function TLinqArray<T>.OrderDescending: ILinqArray<T>;
  559. var
  560. comparer : IComparer<T>;
  561. begin
  562. comparer := TLinqComparer.Create(False);
  563. TArray.Sort<T>(fArray,comparer);
  564. end;
  565. function TLinqArray<T>.Select: TArray<T>;
  566. var
  567. value : T;
  568. begin
  569. //DoOrderBy(fList);
  570. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  571. for value in fArray do
  572. begin
  573. if Validate(value) then Result := Result + [value];
  574. end;
  575. end;
  576. function TLinqArray<T>.SelectFirst: T;
  577. var
  578. value : 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 Exit(value);
  585. end;
  586. end;
  587. function TLinqArray<T>.SelectLast: T;
  588. var
  589. value : T;
  590. found : T;
  591. begin
  592. //DoOrderBy(fList);
  593. if fMatchString.IsEmpty then raise ELinqNotValidExpression.Create('Not valid expression defined!');
  594. for value in fArray do
  595. begin
  596. if Validate(value) then found := value;
  597. end;
  598. Result := found;
  599. end;
  600. function TLinqArray<T>.SelectTop(aLimit: Integer): TArray<T>;
  601. var
  602. i : Integer;
  603. limit : Integer;
  604. begin
  605. if aLimit > High(fArray) then limit := High(fArray)
  606. else limit := aLimit;
  607. SetLength(Result,limit);
  608. for i := Low(fArray) to limit do
  609. begin
  610. Result[i] := fArray[i];
  611. end;
  612. end;
  613. function TLinqArray<T>.Update(const aNewValue: T): Integer;
  614. var
  615. i : Integer;
  616. begin
  617. for i := Low(fArray) to High(fArray) do
  618. begin
  619. if Validate(fArray[i]) then fArray[i] := aNewValue;
  620. end;
  621. end;
  622. function TLinqArray<T>.Validate(aValue: T): Boolean;
  623. var
  624. regEx : TRegEx;
  625. value : TValue;
  626. begin
  627. value := TValue.From<T>(aValue);
  628. if fUseRegEx then
  629. begin
  630. regEx := TRegEx.Create(fMatchString,[roIgnoreCase,roMultiline]);
  631. try
  632. Result := regEx.IsMatch(value.AsString);
  633. except
  634. raise Exception.Create('TLinqArray not valid RegEx!');
  635. end;
  636. end
  637. else
  638. begin
  639. Result := CompareText(fMatchString,value.AsString) = 0;
  640. end;
  641. end;
  642. function TLinqArray<T>.Where(const aMatchString: string; aUseRegEx: Boolean): ILinqArray<T>;
  643. begin
  644. Result := Self;
  645. fMatchString := aMatchString;
  646. fUseRegEx := aUseRegEx;
  647. end;
  648. { TLinqArray<T>.TLinqComparer }
  649. constructor TLinqArray<T>.TLinqComparer.Create(aSortAscending : Boolean);
  650. begin
  651. fSortAscending := aSortAscending;
  652. end;
  653. function TLinqArray<T>.TLinqComparer.Compare(const L, R: T): Integer;
  654. var
  655. valueL : TValue;
  656. valueR : TValue;
  657. hr : Integer;
  658. lr : Integer;
  659. begin
  660. Result := 0;
  661. if fSortAscending then
  662. begin
  663. hr := 1;
  664. lr := -1;
  665. end
  666. else
  667. begin
  668. hr := -1;
  669. lr := 1;
  670. end;
  671. valueL := TValue.From<T>(L);
  672. valueR := TValue.From<T>(R);
  673. if (valueL.IsEmpty) and (not valueR.IsEmpty) then Exit(hr)
  674. else if (not valueL.IsEmpty) and (valueR.IsEmpty) then Exit(lr);
  675. case valueL.Kind of
  676. {$IFNDEF FPC}
  677. tkString,
  678. {$ENDIF}
  679. tkChar, tkWString, tkUString : Result := CompareText(valueL.AsString, valueR.AsString);
  680. tkInteger, tkInt64 :
  681. begin
  682. if valueL.AsInteger > valueR.AsInteger then Result := hr
  683. else if valueL.AsInteger < valueR.AsInteger then Result := lr;
  684. end;
  685. tkFloat :
  686. begin
  687. if valueL.AsExtended > valueR.AsExtended then Result := hr
  688. else if valueL.AsExtended < valueR.AsExtended then Result := lr;
  689. end;
  690. end;
  691. end;
  692. { TLinqArrayHelper }
  693. function TLinqArrayHelper.Add(const aValue : string) : Integer;
  694. begin
  695. SetLength(Self,Length(Self)+1);
  696. Self[High(Self)] := aValue;
  697. Result := High(Self);
  698. end;
  699. function TLinqArrayHelper.AddIfNotExists(const aValue : string) : Integer;
  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 Exit(i);
  706. end;
  707. //if not exists add it
  708. Result := Self.Add(aValue);
  709. end;
  710. function TLinqArrayHelper.Remove(const aValue : string) : Boolean;
  711. var
  712. i : Integer;
  713. begin
  714. for i := Low(Self) to High(Self) do
  715. begin
  716. if CompareText(Self[i],aValue) = 0 then
  717. begin
  718. System.Delete(Self,i,1);
  719. Exit(True);
  720. end;
  721. end;
  722. Result := False;
  723. end;
  724. function TLinqArrayHelper.Any : Boolean;
  725. begin
  726. Result := High(Self) >= 0;
  727. end;
  728. function TLinqArrayHelper.Any(const aValue : string) : Boolean;
  729. var
  730. value : string;
  731. begin
  732. Result := False;
  733. for value in Self do
  734. begin
  735. if CompareText(value,aValue) = 0 then Exit(True)
  736. end;
  737. end;
  738. function TLinqArrayHelper.Any(const aMatchString : string; aUseRegEx : Boolean) : Boolean;
  739. begin
  740. Result := TLinqArray<string>.Create(Self).Any(aMatchString,aUseRegEx);
  741. end;
  742. function TLinqArrayHelper.Where(const aMatchString : string; aUseRegEx : Boolean) : ILinqArray<string>;
  743. begin
  744. Result := TLinqArray<string>.Create(Self).Where(aMatchString,aUseRegEx);
  745. end;
  746. {$ENDIF}
  747. end.