Quick.Linq.pas 22 KB

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