Quick.Lists.pas 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. { ***************************************************************************
  2. Copyright (c) 2016-2019 Kike Pérez
  3. Unit : Quick.Lists
  4. Description : Generic Lists functions
  5. Author : Kike Pérez
  6. Version : 1.1
  7. Created : 04/11/2018
  8. Modified : 07/04/2019
  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.Lists;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Classes,
  26. SysUtils,
  27. RTTI,
  28. TypInfo,
  29. System.Generics.Collections,
  30. System.Generics.Defaults,
  31. Quick.RTTI.Utils,
  32. Quick.Arrays,
  33. Quick.Value;
  34. type
  35. TClassField = (cfField, cfProperty);
  36. TSearchDictionary<TKey,TValue> = class(TObjectDictionary<TKey,TValue>)
  37. private
  38. fIndexName : string;
  39. fFieldName : string;
  40. fClassField : TClassField;
  41. public
  42. property IndexName : string read fIndexName write fIndexName;
  43. property FieldName : string read fFieldName write fFieldName;
  44. property ClassField : TClassField read fClassField write fClassField;
  45. end;
  46. TIndexList<T> = class
  47. private
  48. fList : TList<TSearchDictionary<Variant,T>>;
  49. fDictionaryIndex : TObjectDictionary<string,TSearchDictionary<Variant,T>>;
  50. public
  51. constructor Create;
  52. destructor Destroy; override;
  53. property List : TList<TSearchDictionary<Variant,T>> read fList;
  54. function Get(const aIndexName : string) : TSearchDictionary<Variant,T>;
  55. procedure Add(const aIndexName, aFieldName : string; aClassField : TClassField = cfProperty);
  56. procedure Remove(const aIndexName : string);
  57. end;
  58. TIndexedObjectList<T: class> = class(TList<T>)
  59. private
  60. fOwnsObjects: Boolean;
  61. fIndexes : TIndexList<T>;
  62. protected
  63. procedure Notify(const Value: T; Action: TCollectionNotification); override;
  64. public
  65. constructor Create(aOwnsObjects: Boolean = True); overload;
  66. constructor Create(const aComparer: IComparer<T>; aOwnsObjects: Boolean = True); overload;
  67. constructor Create(const aCollection: TEnumerable<T>; aOwnsObjects: Boolean = True); overload;
  68. destructor Destroy; override;
  69. property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
  70. property Indexes : TIndexList<T> read fIndexes;
  71. function Get(const aIndexName : string; aValue : Variant) : T;
  72. end;
  73. TSearchObjectList<T: class> = class(TObjectList<T>)
  74. public
  75. function Get(const aFieldName: string; const aValue: string; aClassField : TClassField = cfProperty) : T; overload;
  76. function Get(const aFieldName : string; aValue : Int64; aClassField : TClassField = cfProperty) : T; overload;
  77. function Get(const aFieldName : string; aValue : Double; aClassField : TClassField = cfProperty) : T; overload;
  78. function Get(const aFieldName : string; aValue : TDateTime; aClassField : TClassField = cfProperty) : T; overload;
  79. end;
  80. implementation
  81. { TIndexedObjectList<T> }
  82. constructor TIndexedObjectList<T>.Create(aOwnsObjects: Boolean);
  83. begin
  84. inherited Create;
  85. FOwnsObjects := aOwnsObjects;
  86. fIndexes := TIndexList<T>.Create;
  87. end;
  88. constructor TIndexedObjectList<T>.Create(const aComparer: IComparer<T>; aOwnsObjects: Boolean);
  89. begin
  90. inherited Create(aComparer);
  91. FOwnsObjects := aOwnsObjects;
  92. fIndexes := TIndexList<T>.Create;
  93. end;
  94. constructor TIndexedObjectList<T>.Create(const aCollection: TEnumerable<T>; aOwnsObjects: Boolean);
  95. begin
  96. inherited Create(aCollection);
  97. FOwnsObjects := aOwnsObjects;
  98. fIndexes := TIndexList<T>.Create;
  99. end;
  100. procedure TIndexedObjectList<T>.Notify(const Value: T; Action: TCollectionNotification);
  101. var
  102. sindex : TSearchDictionary<Variant,T>;
  103. propvalue : TValue;
  104. begin
  105. inherited;
  106. if Action = cnAdded then
  107. begin
  108. for sindex in fIndexes.List do
  109. begin
  110. try
  111. if sindex.ClassField = TClassField.cfField then propvalue := TRTTI.GetFieldValue(TObject(Value),sindex.FieldName)
  112. else propvalue := TRTTI.GetPropertyValue(TObject(Value),sindex.FieldName);
  113. except
  114. raise Exception.CreateFmt('Cannot add value to "%s" search dictionary!',[sindex.IndexName]);
  115. end;
  116. sindex.Add(propvalue.AsVariant,Value);
  117. end;
  118. end;
  119. //remove object if owned
  120. if OwnsObjects and ((Action = cnRemoved) or (Action = cnExtracted)) then
  121. begin
  122. for sindex in fIndexes.List do
  123. begin
  124. try
  125. if sindex.ClassField = TClassField.cfField then propvalue := TRTTI.GetFieldValue(TObject(Value),sindex.FieldName)
  126. else propvalue := TRTTI.GetPropertyValue(TObject(Value),sindex.FieldName);
  127. except
  128. raise Exception.CreateFmt('Cannot remove value to "%s" search dictionary!',[sindex.IndexName]);
  129. end;
  130. sindex.Remove(propvalue.AsVariant);
  131. end;
  132. Value.DisposeOf;
  133. end;
  134. end;
  135. destructor TIndexedObjectList<T>.Destroy;
  136. begin
  137. inherited;
  138. fIndexes.Free;
  139. end;
  140. function TIndexedObjectList<T>.Get(const aIndexName: string; aValue : Variant): T;
  141. var
  142. sindex : TSearchDictionary<Variant,T>;
  143. begin
  144. Result := nil;
  145. sindex := fIndexes.Get(aIndexName.ToLower);
  146. if sindex <> nil then sindex.TryGetValue(aValue,Result)
  147. else raise Exception.CreateFmt('Index "%s" not found!',[aIndexName]);
  148. end;
  149. { TIndexList<T> }
  150. procedure TIndexList<T>.Add(const aIndexName, aFieldName : string; aClassField : TClassField = cfProperty);
  151. var
  152. sdict : TSearchDictionary<Variant,T>;
  153. begin
  154. if aClassField = TClassField.cfField then
  155. begin
  156. if not TRTTI.FieldExists(TypeInfo(T),aFieldName) then raise Exception.CreateFmt('Not found field "%s" to create a search dictionary!',[aFieldName]);
  157. end
  158. else
  159. begin
  160. if not TRTTI.PropertyExists(TypeInfo(T),aFieldName) then raise Exception.CreateFmt('Not found property "%s" to create a search dictionary!',[aFieldName]);
  161. end;
  162. sdict := TSearchDictionary<Variant,T>.Create;
  163. sdict.IndexName := aIndexName;
  164. sdict.FieldName := aFieldName;
  165. sdict.ClassField := aClassField;
  166. fList.Add(sdict);
  167. fDictionaryIndex.Add(aIndexName.ToLower,sdict);
  168. end;
  169. procedure TIndexList<T>.Remove(const aIndexName: string);
  170. var
  171. sdict : TSearchDictionary<Variant,T>;
  172. begin
  173. if not fDictionaryIndex.ContainsKey(aIndexName) then raise Exception.CreateFmt('Cannot remove an inexistent "%s" search dictionary!',[aIndexName]);
  174. fList.Remove(sdict);
  175. fDictionaryIndex.Remove(aIndexName.ToLower);
  176. sdict.Free;
  177. end;
  178. constructor TIndexList<T>.Create;
  179. begin
  180. fList := TList<TSearchDictionary<Variant,T>>.Create;
  181. fDictionaryIndex := TObjectDictionary<string,TSearchDictionary<Variant,T>>.Create;
  182. end;
  183. destructor TIndexList<T>.Destroy;
  184. var
  185. sindex : TSearchDictionary<Variant,T>;
  186. begin
  187. for sindex in fList do sindex.Free;
  188. fList.Free;
  189. fDictionaryIndex.Free;
  190. inherited;
  191. end;
  192. function TIndexList<T>.Get(const aIndexName: string): TSearchDictionary<Variant, T>;
  193. begin
  194. Result := nil;
  195. fDictionaryIndex.TryGetValue(aIndexName,Result);
  196. end;
  197. { TSearchObjectList<T> }
  198. function TSearchObjectList<T>.Get(const aFieldName: string; const aValue: string; aClassField : TClassField = cfProperty): T;
  199. var
  200. val : T;
  201. begin
  202. Result := nil;
  203. for val in List do
  204. begin
  205. if aClassField = TClassField.cfField then
  206. begin
  207. if TRTTI.GetFieldValue(TObject(val),aFieldName).AsString = aValue then Exit(val);
  208. end
  209. else
  210. begin
  211. if GetStrProp(TObject(val),aFieldName) = aValue then Exit(val);
  212. end;
  213. end;
  214. end;
  215. function TSearchObjectList<T>.Get(const aFieldName: string; aValue: Int64; aClassField : TClassField = cfProperty): T;
  216. var
  217. val : T;
  218. begin
  219. Result := nil;
  220. for val in List do
  221. begin
  222. if aClassField = TClassField.cfField then
  223. begin
  224. if TRTTI.GetFieldValue(TObject(val),aFieldName).AsInt64 = aValue then Exit(val);
  225. end
  226. else
  227. begin
  228. if GetInt64Prop(TObject(val),aFieldName) = aValue then Exit(val);
  229. end;
  230. end;
  231. end;
  232. function TSearchObjectList<T>.Get(const aFieldName: string; aValue: Double; aClassField : TClassField = cfProperty): T;
  233. var
  234. val : T;
  235. begin
  236. Result := nil;
  237. for val in List do
  238. begin
  239. if aClassField = TClassField.cfField then
  240. begin
  241. if TRTTI.GetFieldValue(TObject(val),aFieldName).AsExtended = aValue then Exit(val);
  242. end
  243. else
  244. begin
  245. if GetFloatProp(TObject(val),aFieldName) = aValue then Exit(val);
  246. end;
  247. end;
  248. end;
  249. function TSearchObjectList<T>.Get(const aFieldName: string; aValue: TDateTime; aClassField : TClassField = cfProperty): T;
  250. var
  251. val : T;
  252. begin
  253. Result := nil;
  254. for val in List do
  255. begin
  256. if aClassField = TClassField.cfField then
  257. begin
  258. if TRTTI.GetFieldValue(TObject(val),aFieldName).AsExtended = aValue then Exit(val);
  259. end
  260. else
  261. begin
  262. if GetFloatProp(TObject(val),aFieldName) = aValue then Exit(val);
  263. end;
  264. end;
  265. end;
  266. end.