Quick.Linq.pas 22 KB

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