Coverage report for Casbin.Policy.
Generated at 18/02/2019 12:10:19 by DelphiCodeCoverage - an open source tool for Delphi Code Coverage.
Statistics for ArrayHelper.pas
Number of lines covered | 219 |
Number of lines with code gen | 267 |
Line coverage | 82% |
1 | unit ArrayHelper; |
2 | |
3 | |
4 | /////////////////////////////////////////////////////////////////////////////// |
5 | // |
6 | // ArrayHelper version 1.3 |
7 | // extends class TArray and add TArrayRecord<T> to make dynamic arrays |
8 | // as simple, as TList |
9 | // |
10 | // Copyright(c) 2017 by Willi Commer (wcs) |
11 | // Licence GNU |
12 | // |
13 | // Dynamic arrays are smart because its memore usage is handled by the memory |
14 | // manager. But the funtion libraries are lean and differs from object based. |
15 | // Based on TArray class, that gives Sort and Binary search, this unit will |
16 | // extend TArray with functions available for TList or TStrings. |
17 | // The next level is TArrayRecord<T> record type. It wraps a record around |
18 | // the dynamic array. This give us the ability to use dynamic arrays like |
19 | // objects with out the pain to organize the final Free call. |
20 | // |
21 | // var |
22 | // A: TArrayRecord<string>; |
23 | // S: string; |
24 | // begin |
25 | // A.SetValues(['a','b','c']); |
26 | // A.Add('d'); |
27 | // assert( A.Count = 4 ); // same as length(A.Items); |
28 | // assert( A[1] = 'b' ); |
29 | // assert( A.IndexOf('a') = 0 ); |
30 | // for S in A do |
31 | // .. |
32 | // |
33 | // For more examples see procedure Test_All_Helper_Functions |
34 | // For updates check https://github.com/WilliCommer/ArrayHelper |
35 | // |
36 | // |
37 | // History: |
38 | // version 1.3 |
39 | // Enumeration added |
40 | // new functions 'Unique' and 'CopyArray' |
41 | // |
42 | // version 1.2 |
43 | // TArrayRecord<T> |
44 | // |
45 | /////////////////////////////////////////////////////////////////////////////// |
46 | |
47 | |
48 | |
49 | { $DEFINE TEST_FUNCTION} // change to active test function |
50 | |
51 | |
52 | interface |
53 | uses |
54 | System.Classes, System.SysUtils, System.RTLConsts, |
55 | System.Generics.Defaults, System.Generics.Collections; |
56 | |
57 | |
58 | type |
59 | |
60 | // callback function for function ForEach |
61 | TArrayForEachCallback<T> = reference to procedure(var Value: T; Index: integer); |
62 | |
63 | // callback function for function Map |
64 | TArrayMapCallback<T> = reference to function(var Value: T; Index: integer): boolean; |
65 | |
66 | // callback function for function MapTo |
67 | TArrayConvert<T,TTo> = reference to function(const Value: T): TTo; |
68 | |
69 | // callback function for function Find |
70 | TArrayFindCallback<T> = reference to function(const Value: T): boolean; |
71 | |
72 | |
73 | |
74 | // extends class TArray |
75 | TArrayHelper = class helper for TArray |
76 | // add item to array |
77 | class function Add<T>(var Values: TArray<T>; Item: T): integer; static; |
78 | |
79 | // delete item at index |
80 | class procedure Delete<T>(var Values: TArray<T>; Index: integer); static; |
81 | |
82 | // insert item at index |
83 | class procedure Insert<T>(var Values: TArray<T>; Index: integer; Value: T); static; |
84 | |
85 | // append array |
86 | class procedure AddRange<T>(var Values: TArray<T>; const ValuesToInsert: array of T); static; |
87 | |
88 | // insert array at index |
89 | class procedure InsertRange<T>(var Values: TArray<T>; Index: Integer; const ValuesToInsert: array of T); static; |
90 | |
91 | // get index of equal item |
92 | class function IndexOf<T>(var Values: TArray<T>; Item: T): integer; overload; static; |
93 | |
94 | // get index of equal item (using IComparer) |
95 | class function IndexOf<T>(var Values: TArray<T>; Item: T; const Comparer: IComparer<T>): integer; overload; static; |
96 | |
97 | // get index of maximal item |
98 | class function IndexOfMax<T>(var Values: TArray<T>): integer; overload; static; |
99 | |
100 | // get index of maximal item (using IComparer) |
101 | class function IndexOfMax<T>(var Values: TArray<T>; const Comparer: IComparer<T>): integer; overload; static; |
102 | |
103 | // get index of minimal item |
104 | class function IndexOfMin<T>(var Values: TArray<T>): integer; overload; static; |
105 | |
106 | // get index of minimal item (using IComparer) |
107 | class function IndexOfMin<T>(var Values: TArray<T>; const Comparer: IComparer<T>): integer; overload; static; |
108 | |
109 | // is a equal item is member of values |
110 | class function Contains<T>(var Values: TArray<T>; Item: T): boolean; overload; static; |
111 | |
112 | // is a equal item is member of values (using IComparer) |
113 | class function Contains<T>(var Values: TArray<T>; Item: T; const Comparer: IComparer<T>): boolean; overload; static; |
114 | |
115 | // compare two arrays |
116 | class function Compare<T>(const Values, ValuesToCompare: array of T): boolean; overload; static; |
117 | |
118 | // compare two arrays (using IComparer) |
119 | class function Compare<T>(const Values, ValuesToCompare: array of T; const Comparer: IComparer<T>): boolean; overload; static; |
120 | |
121 | // ForEach |
122 | class procedure ForEach<T>(var Values: TArray<T>; const Callback: TArrayForEachCallback<T>); static; |
123 | |
124 | // find with callback |
125 | class function Find<T>(const Values: TArray<T>; const Callback: TArrayFindCallback<T>; const StartIndex: integer = 0): integer; overload; static; |
126 | |
127 | // return an array filtered and converted by callback function |
128 | class function Map<T>(const Values: TArray<T>; const Callback: TArrayMapCallback<T>): TArray<T>; static; |
129 | |
130 | // return the array as TList |
131 | class procedure List<T>(const Values: TArray<T>; var ValList: TList<T>); static; |
132 | |
133 | {$IFDEF TEST_FUNCTION} |
134 | // test, debug and example function |
135 | class procedure Test_All_Helper_Functions; |
136 | {$ENDIF TEST_FUNCTION} |
137 | |
138 | end; |
139 | |
140 | |
141 | type |
142 | TArrayRecord<T> = record |
143 | strict private type |
144 | TEnumerator = class |
145 | private |
146 | FValue: ^TArrayRecord<T>; |
147 | FIndex: integer; |
148 | function GetCurrent: T; |
149 | public |
150 | constructor Create(var AValue: TArrayRecord<T>); |
151 | function MoveNext: Boolean; |
152 | property Current: T read GetCurrent; |
153 | end; |
154 | public |
155 | function GetEnumerator(): TEnumerator; |
156 | private |
157 | function GetCount: integer; |
158 | procedure SetCount(const Value: integer); |
159 | function GetItemAt(Index: integer): T; |
160 | procedure SetItemAt(Index: integer; Value: T); |
161 | public |
162 | Items: TArray<T>; |
163 | property Count: integer read GetCount write SetCount; |
164 | property ItemAt[Index: Integer]: T read GetItemAt write SetItemAt; default; |
165 | |
166 | constructor Create(ACapacity: integer); overload; |
167 | constructor Create(const AValues: array of T); overload; |
168 | procedure Clear; |
169 | procedure SetItems(const Values: array of T); |
170 | function Add(const Value: T): integer; |
171 | procedure Delete(Index: integer); |
172 | procedure Insert(Index: integer; Value: T); |
173 | function Remove(const AItem: T): boolean; |
174 | function AddIfNotContains(const AItem: T): boolean; |
175 | |
176 | procedure AddRange(const ValuesToInsert: array of T); overload; |
177 | procedure AddRange(const ValuesToInsert: TArrayRecord<T>); overload; |
178 | |
179 | procedure InsertRange(Index: Integer; const ValuesToInsert: array of T); overload; |
180 | procedure InsertRange(Index: Integer; const ValuesToInsert: TArrayRecord<T>); overload; |
181 | |
182 | function IndexOf(Item: T): integer; overload; |
183 | function IndexOf(Item: T; const Comparer: IComparer<T>): integer; overload; |
184 | |
185 | function IndexOfMax: integer; overload; |
186 | function IndexOfMax(const Comparer: IComparer<T>): integer; overload; |
187 | function IndexOfMin: integer; overload; |
188 | function IndexOfMin(const Comparer: IComparer<T>): integer; overload; |
189 | |
190 | function Contains(Item: T): boolean; overload; |
191 | function Contains(Item: T; const Comparer: IComparer<T>): boolean; overload; |
192 | |
193 | function Compare(const ValuesToCompare: array of T): boolean; overload; |
194 | function Compare(const ValuesToCompare: array of T; const Comparer: IComparer<T>): boolean; overload; |
195 | function Compare(const ValuesToCompare: TArrayRecord<T>): boolean; overload; |
196 | function Compare(const ValuesToCompare: TArrayRecord<T>; const Comparer: IComparer<T>): boolean; overload; |
197 | |
198 | procedure ForEach(const Callback: TArrayForEachCallback<T>); |
199 | function Find(const Callback: TArrayFindCallback<T>; const StartIndex: integer = 0): integer; overload; |
200 | function Map(const Callback: TArrayMapCallback<T>): TArrayRecord<T>; |
201 | function Convert<TTo>(const Callback: TArrayConvert<T,TTo>): TArrayRecord<TTo>; |
202 | |
203 | procedure Sort; overload; |
204 | procedure Sort(const AComparer: IComparer<T>); overload; |
205 | procedure Sort(const AComparer: IComparer<T>; AIndex, ACount: Integer); overload; |
206 | function BinarySearch(const AItem: T; out AFoundIndex: Integer; const AComparer: IComparer<T>; |
207 | AIndex, ACount: Integer): Boolean; overload; |
208 | function BinarySearch(const AItem: T; out AFoundIndex: Integer; const AComparer: IComparer<T>): Boolean; overload; |
209 | function BinarySearch(const AItem: T; out AFoundIndex: Integer): Boolean; overload; |
210 | |
211 | procedure Unique; // remove duplicates |
212 | function CopyArray(FromIndex: integer; Count: integer = -1): TArrayRecord<T>; // return array slice |
213 | |
214 | procedure List(var ValList: TList<T>); |
215 | // operator overloads |
216 | class operator Equal(const L, R: TArrayRecord<T>): boolean; |
217 | class operator NotEqual(const L, R: TArrayRecord<T>): boolean; |
218 | end; |
219 | |
220 | |
221 | |
222 | implementation |
223 | |
224 | |
225 | { TArrayHelper } |
226 | |
227 | class function TArrayHelper.Add<T>(var Values: TArray<T>; Item: T): integer; |
228 | begin |
229 | Result := Length(Values); |
230 | SetLength(Values,Result+1); |
231 | Values[Result] := Item; |
232 | end; |
233 | |
234 | |
235 | class procedure TArrayHelper.Delete<T>(var Values: TArray<T>; Index: integer); |
236 | var |
237 | I: Integer; |
238 | begin |
239 | if (Index < Low(Values)) or (Index > High(Values)) then |
240 | raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); |
241 | for I := Index+1 to High(Values) do |
242 | Values[I-1] := Values[I]; |
243 | SetLength(Values, length(Values)-1); |
244 | end; |
245 | |
246 | |
247 | class procedure TArrayHelper.Insert<T>(var Values: TArray<T>; Index: integer; Value: T); |
248 | var |
249 | I,H: Integer; |
250 | begin |
251 | if (Index < Low(Values)) or (Index > length(Values)) then |
252 | raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); |
253 | H := High(Values); |
254 | SetLength(Values, length(Values)+1); |
255 | for I := H downto Index do |
256 | Values[I+1] := Values[I]; |
257 | Values[Index] := Value; |
258 | end; |
259 | |
260 | |
261 | class procedure TArrayHelper.InsertRange<T>(var Values: TArray<T>; Index: Integer; const ValuesToInsert: array of T); |
262 | var |
263 | I,L,H: Integer; |
264 | begin |
265 | L := length(ValuesToInsert); |
266 | if L = 0 then EXIT; |
267 | if (Index < Low(Values)) or (Index > length(Values)) then |
268 | raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); |
269 | H := High(Values); |
270 | SetLength(Values, length(Values) + L); |
271 | for I := H downto Index do |
272 | Values[I+L] := Values[I]; |
273 | for I := Low(ValuesToInsert) to High(ValuesToInsert) do |
274 | Values[Index+I] := ValuesToInsert[I]; |
275 | end; |
276 | |
277 | |
278 | class procedure TArrayHelper.List<T>(const Values: TArray<T>; var ValList: |
279 | TList<T>); |
280 | var |
281 | I: Integer; |
282 | begin |
283 | if not Assigned(ValList) then |
284 | raise Exception.Create('ValList is nil'); |
285 | ValList.Clear; |
286 | for I := Low(Values) to High(Values) do |
287 | ValList.Add(Values[I]); |
288 | end; |
289 | |
290 | class procedure TArrayHelper.AddRange<T>(var Values: TArray<T>; const ValuesToInsert: array of T); |
291 | var |
292 | I,Index: Integer; |
293 | begin |
294 | Index := length(Values); |
295 | SetLength(Values, length(Values) + length(ValuesToInsert)); |
296 | for I := Low(ValuesToInsert) to High(ValuesToInsert) do |
297 | Values[Index+I] := ValuesToInsert[I]; |
298 | end; |
299 | |
300 | |
301 | class function TArrayHelper.IndexOf<T>(var Values: TArray<T>; Item: T; const Comparer: IComparer<T>): integer; |
302 | begin |
303 | for Result := Low(Values) to High(Values) do |
304 | if Comparer.Compare(Values[Result], Item) = 0 then EXIT; |
305 | Result := -1; |
306 | end; |
307 | |
308 | class function TArrayHelper.IndexOf<T>(var Values: TArray<T>; Item: T): integer; |
309 | begin |
310 | Result := IndexOf<T>(Values, Item, TComparer<T>.Default); |
311 | end; |
312 | |
313 | |
314 | class function TArrayHelper.IndexOfMax<T>(var Values: TArray<T>): integer; |
315 | begin |
316 | Result := IndexOfMax<T>(Values, TComparer<T>.Default); |
317 | end; |
318 | |
319 | class function TArrayHelper.IndexOfMax<T>(var Values: TArray<T>; const Comparer: IComparer<T>): integer; |
320 | var |
321 | I: Integer; |
322 | begin |
323 | if length(Values) = 0 then |
324 | raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); |
325 | Result := 0; |
326 | for I := 1 to High(Values) do |
327 | if Comparer.Compare(Values[I], Values[Result]) > 0 then |
328 | Result := I; |
329 | end; |
330 | |
331 | class function TArrayHelper.IndexOfMin<T>(var Values: TArray<T>): integer; |
332 | begin |
333 | Result := IndexOfMin<T>(Values, TComparer<T>.Default); |
334 | end; |
335 | |
336 | class function TArrayHelper.IndexOfMin<T>(var Values: TArray<T>; const Comparer: IComparer<T>): integer; |
337 | var |
338 | I: Integer; |
339 | begin |
340 | if length(Values) = 0 then |
341 | raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); |
342 | Result := 0; |
343 | for I := 1 to High(Values) do |
344 | if Comparer.Compare(Values[I], Values[Result]) < 0 then |
345 | Result := I; |
346 | end; |
347 | |
348 | |
349 | |
350 | |
351 | |
352 | class function TArrayHelper.Contains<T>(var Values: TArray<T>; Item: T; const Comparer: IComparer<T>): boolean; |
353 | begin |
354 | Result := IndexOf<T>(Values, Item, Comparer) <> -1; |
355 | end; |
356 | |
357 | |
358 | |
359 | class function TArrayHelper.Contains<T>(var Values: TArray<T>; Item: T): boolean; |
360 | begin |
361 | Result := Contains<T>(Values, Item, TComparer<T>.Default); |
362 | end; |
363 | |
364 | |
365 | |
366 | class function TArrayHelper.Compare<T>(const Values, ValuesToCompare: array of T; const Comparer: IComparer<T>): boolean; |
367 | var |
368 | I: Integer; |
369 | begin |
370 | if length(Values) <> length(ValuesToCompare) then EXIT( FALSE ); |
371 | for I := Low(Values) to High(Values) do |
372 | if Comparer.Compare(Values[I], ValuesToCompare[I]) <> 0 then EXIT( FALSE ); |
373 | Result := TRUE; |
374 | end; |
375 | |
376 | |
377 | class function TArrayHelper.Compare<T>(const Values, ValuesToCompare: array of T): boolean; |
378 | begin |
379 | Result := Compare<T>(Values, ValuesToCompare, TComparer<T>.Default); |
380 | end; |
381 | |
382 | |
383 | |
384 | |
385 | class procedure TArrayHelper.ForEach<T>(var Values: TArray<T>; const Callback: TArrayForEachCallback<T>); |
386 | var |
387 | I: Integer; |
388 | begin |
389 | for I := Low(Values) to High(Values) do |
390 | Callback(Values[I], I); |
391 | end; |
392 | |
393 | |
394 | |
395 | class function TArrayHelper.Find<T>(const Values: TArray<T>; const Callback: TArrayFindCallback<T>; |
396 | const StartIndex: integer): integer; |
397 | begin |
398 | if (length(Values) = 0) or (StartIndex < 0) or (StartIndex > High(Values)) then EXIT( -1 ); |
399 | for Result := StartIndex to High(Values) do |
400 | if Callback(Values[Result]) then EXIT; |
401 | Result := -1; |
402 | end; |
403 | |
404 | |
405 | |
406 | class function TArrayHelper.Map<T>(const Values: TArray<T>; const Callback: TArrayMapCallback<T>): TArray<T>; |
407 | var |
408 | Item: T; |
409 | I: Integer; |
410 | begin |
411 | Result := NIL; |
412 | for I := Low(Values) to High(Values) do |
413 | begin |
414 | Item := Values[I]; |
415 | if Callback(Item, I) then |
416 | Add<T>(Result, Item); |
417 | end; |
418 | end; |
419 | |
420 | |
421 | |
422 | |
423 | |
424 | |
425 | { TArrayRecord<T>.TEnumerator } |
426 | |
427 | constructor TArrayRecord<T>.TEnumerator.Create(var AValue: TArrayRecord<T>); |
428 | begin |
429 | FValue := @AValue; |
430 | FIndex := -1; |
431 | end; |
432 | |
433 | function TArrayRecord<T>.TEnumerator.GetCurrent: T; |
434 | begin |
435 | Result := FValue^.Items[FIndex]; |
436 | end; |
437 | |
438 | function TArrayRecord<T>.TEnumerator.MoveNext: Boolean; |
439 | begin |
440 | Result := FIndex < High(FValue^.Items); |
441 | Inc(FIndex); |
442 | end; |
443 | |
444 | |
445 | |
446 | { TArrayRecord<T> } |
447 | |
448 | |
449 | constructor TArrayRecord<T>.Create(ACapacity: integer); |
450 | begin |
451 | SetLength(Items, ACapacity); |
452 | end; |
453 | |
454 | constructor TArrayRecord<T>.Create(const AValues: array of T); |
455 | begin |
456 | SetLength(Items, 0); |
457 | AddRange(AValues); |
458 | end; |
459 | |
460 | procedure TArrayRecord<T>.Clear; |
461 | begin |
462 | SetLength(Items, 0); |
463 | end; |
464 | |
465 | |
466 | |
467 | class operator TArrayRecord<T>.Equal(const L, R: TArrayRecord<T>): boolean; |
468 | begin |
469 | Result := L.Compare(R); |
470 | end; |
471 | |
472 | |
473 | class operator TArrayRecord<T>.NotEqual(const L, R: TArrayRecord<T>): boolean; |
474 | begin |
475 | Result := not L.Compare(R); |
476 | end; |
477 | |
478 | |
479 | |
480 | function TArrayRecord<T>.GetCount: integer; |
481 | begin |
482 | Result := length(Items); |
483 | end; |
484 | |
485 | function TArrayRecord<T>.GetEnumerator: TEnumerator; |
486 | begin |
487 | Result := TEnumerator.Create(Self); |
488 | end; |
489 | |
490 | procedure TArrayRecord<T>.SetCount(const Value: integer); |
491 | begin |
492 | SetLength(Items, Value); |
493 | end; |
494 | |
495 | procedure TArrayRecord<T>.SetItemAt(Index: integer; Value: T); |
496 | begin |
497 | Items[Index] := Value; |
498 | end; |
499 | |
500 | procedure TArrayRecord<T>.SetItems(const Values: array of T); |
501 | begin |
502 | SetLength(Items, 0); |
503 | AddRange(Values); |
504 | end; |
505 | |
506 | function TArrayRecord<T>.GetItemAt(Index: integer): T; |
507 | begin |
508 | Result := Items[Index]; |
509 | end; |
510 | |
511 | |
512 | procedure TArrayRecord<T>.AddRange(const ValuesToInsert: array of T); |
513 | begin |
514 | TArray.AddRange<T>(Items, ValuesToInsert); |
515 | end; |
516 | |
517 | procedure TArrayRecord<T>.AddRange(const ValuesToInsert: TArrayRecord<T>); |
518 | begin |
519 | TArray.AddRange<T>(Items, ValuesToInsert.Items); |
520 | end; |
521 | |
522 | |
523 | |
524 | function TArrayRecord<T>.BinarySearch(const AItem: T; out AFoundIndex: Integer; const AComparer: IComparer<T>; AIndex, |
525 | ACount: Integer): Boolean; |
526 | begin |
527 | Result := TArray.BinarySearch<T>(Items, AItem, AFoundIndex, AComparer, AIndex, ACount); |
528 | end; |
529 | |
530 | function TArrayRecord<T>.BinarySearch(const AItem: T; out AFoundIndex: Integer; const AComparer: IComparer<T>): Boolean; |
531 | begin |
532 | Result := TArray.BinarySearch<T>(Items, AItem, AFoundIndex, AComparer); |
533 | end; |
534 | |
535 | function TArrayRecord<T>.BinarySearch(const AItem: T; out AFoundIndex: Integer): Boolean; |
536 | begin |
537 | Result := TArray.BinarySearch<T>(Items, AItem, AFoundIndex); |
538 | end; |
539 | |
540 | |
541 | procedure TArrayRecord<T>.Delete(Index: integer); |
542 | begin |
543 | TArray.Delete<T>(Items, Index); |
544 | end; |
545 | |
546 | |
547 | |
548 | function TArrayRecord<T>.Remove(const AItem: T): boolean; |
549 | var |
550 | I: integer; |
551 | begin |
552 | I := IndexOf(AItem); |
553 | if I < 0 then |
554 | Result := FALSE |
555 | else |
556 | begin |
557 | Delete(I); |
558 | Result := TRUE; |
559 | end; |
560 | end; |
561 | |
562 | |
563 | function TArrayRecord<T>.AddIfNotContains(const AItem: T): boolean; |
564 | begin |
565 | Result := not Contains(AItem); |
566 | if not Result then |
567 | Add(AItem); |
568 | end; |
569 | |
570 | |
571 | |
572 | function TArrayRecord<T>.Find(const Callback: TArrayFindCallback<T>; const StartIndex: integer): integer; |
573 | begin |
574 | Result := TArray.Find<T>(Items, Callback, StartIndex); |
575 | end; |
576 | |
577 | procedure TArrayRecord<T>.ForEach(const Callback: TArrayForEachCallback<T>); |
578 | begin |
579 | TArray.ForEach<T>(Items, Callback); |
580 | end; |
581 | |
582 | |
583 | |
584 | function TArrayRecord<T>.Compare(const ValuesToCompare: TArrayRecord<T>): boolean; |
585 | begin |
586 | Result := TArray.Compare<T>(Items, ValuesToCompare.Items); |
587 | end; |
588 | |
589 | function TArrayRecord<T>.Compare(const ValuesToCompare: TArrayRecord<T>; const Comparer: IComparer<T>): boolean; |
590 | begin |
591 | Result := TArray.Compare<T>(Items, ValuesToCompare.Items, Comparer); |
592 | end; |
593 | |
594 | function TArrayRecord<T>.Compare(const ValuesToCompare: array of T): boolean; |
595 | begin |
596 | Result := TArray.Compare<T>(Items, ValuesToCompare); |
597 | end; |
598 | |
599 | function TArrayRecord<T>.Compare(const ValuesToCompare: array of T; const Comparer: IComparer<T>): boolean; |
600 | begin |
601 | Result := TArray.Compare<T>(Items, ValuesToCompare, Comparer); |
602 | end; |
603 | |
604 | |
605 | |
606 | |
607 | function TArrayRecord<T>.Contains(Item: T; const Comparer: IComparer<T>): boolean; |
608 | begin |
609 | Result := TArray.Contains<T>(Items, Item, Comparer); |
610 | end; |
611 | |
612 | |
613 | function TArrayRecord<T>.Contains(Item: T): boolean; |
614 | begin |
615 | Result := TArray.Contains<T>(Items, Item); |
616 | end; |
617 | |
618 | |
619 | |
620 | |
621 | function TArrayRecord<T>.IndexOf(Item: T; const Comparer: IComparer<T>): integer; |
622 | begin |
623 | Result := TArray.IndexOf<T>(Items, Item, Comparer); |
624 | end; |
625 | |
626 | |
627 | function TArrayRecord<T>.IndexOfMax: integer; |
628 | begin |
629 | Result := TArray.IndexOfMax<T>(Items); |
630 | end; |
631 | |
632 | function TArrayRecord<T>.IndexOfMax(const Comparer: IComparer<T>): integer; |
633 | begin |
634 | Result := TArray.IndexOfMax<T>(Items, Comparer); |
635 | end; |
636 | |
637 | function TArrayRecord<T>.IndexOfMin: integer; |
638 | begin |
639 | Result := TArray.IndexOfMin<T>(Items); |
640 | end; |
641 | |
642 | function TArrayRecord<T>.IndexOfMin(const Comparer: IComparer<T>): integer; |
643 | begin |
644 | Result := TArray.IndexOfMin<T>(Items, Comparer); |
645 | end; |
646 | |
647 | function TArrayRecord<T>.IndexOf(Item: T): integer; |
648 | begin |
649 | Result := TArray.IndexOf<T>(Items, Item); |
650 | end; |
651 | |
652 | |
653 | procedure TArrayRecord<T>.Insert(Index: integer; Value: T); |
654 | begin |
655 | TArray.Insert<T>(Items, Index, Value); |
656 | end; |
657 | |
658 | |
659 | |
660 | procedure TArrayRecord<T>.InsertRange(Index: Integer; const ValuesToInsert: TArrayRecord<T>); |
661 | begin |
662 | TArray.InsertRange<T>(Items, Index, ValuesToInsert.Items); |
663 | end; |
664 | |
665 | procedure TArrayRecord<T>.List(var ValList: TList<T>); |
666 | begin |
667 | TArray.List<T>(Items, ValList); |
668 | end; |
669 | |
670 | procedure TArrayRecord<T>.InsertRange(Index: Integer; const ValuesToInsert: array of T); |
671 | begin |
672 | TArray.InsertRange<T>(Items, Index, ValuesToInsert); |
673 | end; |
674 | |
675 | |
676 | |
677 | function TArrayRecord<T>.Map(const Callback: TArrayMapCallback<T>): TArrayRecord<T>; |
678 | begin |
679 | Result.Items := TArray.Map<T>(Items, Callback); |
680 | end; |
681 | |
682 | |
683 | |
684 | function TArrayRecord<T>.Convert<TTo>(const Callback: TArrayConvert<T,TTo>): TArrayRecord<TTo>; |
685 | var |
686 | I: Integer; |
687 | begin |
688 | Result.Clear; |
689 | for I := Low(Items) to High(Items) do |
690 | Result.Add(Callback(Items[I])); |
691 | end; |
692 | |
693 | |
694 | |
695 | function TArrayRecord<T>.CopyArray(FromIndex: integer; Count: integer): TArrayRecord<T>; |
696 | var |
697 | I: Integer; |
698 | begin |
699 | Result.Clear; |
700 | if Count < 0 then |
701 | Count := length(Items); |
702 | if length(Items) < (FromIndex + Count) then |
703 | Count := length(Items) - FromIndex; |
704 | if Count > 0 then |
705 | begin |
706 | SetLength(Result.Items, Count); |
707 | for I := 0 to Count-1 do |
708 | Result.Items[I] := Items[I + FromIndex]; |
709 | end; |
710 | end; |
711 | |
712 | |
713 | |
714 | procedure TArrayRecord<T>.Sort; |
715 | begin |
716 | TArray.Sort<T>(Items); |
717 | end; |
718 | |
719 | procedure TArrayRecord<T>.Sort(const AComparer: IComparer<T>); |
720 | begin |
721 | TArray.Sort<T>(Items, AComparer); |
722 | end; |
723 | |
724 | procedure TArrayRecord<T>.Sort(const AComparer: IComparer<T>; AIndex, ACount: Integer); |
725 | begin |
726 | TArray.Sort<T>(Items, AComparer, AIndex, ACount); |
727 | end; |
728 | |
729 | function TArrayRecord<T>.Add(const Value: T): integer; |
730 | begin |
731 | Result := TArray.Add<T>(Items, Value); |
732 | end; |
733 | |
734 | procedure TArrayRecord<T>.Unique; |
735 | var |
736 | Hash: TDictionary<T,integer>; |
737 | I: Integer; |
738 | begin |
739 | Hash := TDictionary<T,integer>.Create(length(Items)); |
740 | try |
741 | for I := Low(Items) to High(Items) do |
742 | Hash.AddOrSetValue(Items[I], 0); |
743 | Items := Hash.Keys.ToArray; |
744 | finally |
745 | Hash.Free; |
746 | end; |
747 | end; |
748 | |
749 | |
750 | |
751 | {$IFDEF TEST_FUNCTION} |
752 | |
753 | |
754 | type |
755 | TTestRecord = record |
756 | Name: string; |
757 | Age: integer; |
758 | constructor Create(AName: string; AAge: integer); |
759 | class function NameComparer: IComparer<TTestRecord>; static; |
760 | class function AgeComparer: IComparer<TTestRecord>; static; |
761 | class function ConvertToNames(const Value: TTestRecord): string; static; |
762 | class function ConvertToAges(const Value: TTestRecord): integer; static; |
763 | end; |
764 | |
765 | constructor TTestRecord.Create(AName: string; AAge: integer); |
766 | begin |
767 | Name := AName; |
768 | Age := AAge; |
769 | end; |
770 | |
771 | class function TTestRecord.ConvertToNames(const Value: TTestRecord): string; |
772 | begin |
773 | Result := Value.Name; |
774 | end; |
775 | |
776 | class function TTestRecord.ConvertToAges(const Value: TTestRecord): integer; |
777 | begin |
778 | Result := Value.Age; |
779 | end; |
780 | |
781 | class function TTestRecord.AgeComparer: IComparer<TTestRecord>; |
782 | begin |
783 | Result := TComparer<TTestRecord>.Construct( |
784 | function(const Left, Right: TTestRecord): Integer |
785 | begin |
786 | Result := TComparer<integer>.Default.Compare(Left.Age, Right.Age); |
787 | end |
788 | ); |
789 | end; |
790 | |
791 | class function TTestRecord.NameComparer: IComparer<TTestRecord>; |
792 | begin |
793 | Result := TComparer<TTestRecord>.Construct( |
794 | function(const Left, Right: TTestRecord): Integer |
795 | begin |
796 | Result := TComparer<string>.Default.Compare(Left.Name, Right.Name); |
797 | end |
798 | ); |
799 | end; |
800 | |
801 | |
802 | procedure Test_TestRecord; |
803 | var |
804 | List: TArrayRecord<TTestRecord>; |
805 | StrList: TArrayRecord<string>; |
806 | I: integer; |
807 | begin |
808 | // create list |
809 | List.Clear; |
810 | List.Add( TTestRecord.Create('Jack', 26) ); |
811 | List.Add( TTestRecord.Create('Anton', 28) ); |
812 | List.Add( TTestRecord.Create('Barbie', 50) ); |
813 | List.Add( TTestRecord.Create('Mickey Mouse', 90) ); |
814 | |
815 | // sort by name |
816 | List.Sort( TTestRecord.NameComparer ); |
817 | // convert to string array |
818 | |
819 | StrList := List.Convert<string>(TTestRecord.ConvertToNames); |
820 | assert( StrList.Compare(['Anton','Barbie','Jack','Mickey Mouse']) ); |
821 | |
822 | // convert to integer array |
823 | assert( List.Convert<integer>(TTestRecord.ConvertToAges).Compare([28,50,26,90]) ); |
824 | |
825 | // sort by age |
826 | List.Sort( TTestRecord.AgeComparer ); |
827 | assert( List[0].Name = 'Jack' ); |
828 | |
829 | // IndexOf Min / Max |
830 | assert( List.IndexOfMax(TTestRecord.AgeComparer) = 3 ); |
831 | assert( List.IndexOfMin(TTestRecord.AgeComparer) = 0 ); |
832 | |
833 | I := List.IndexOfMax(TTestRecord.NameComparer); |
834 | assert( List[I].Name = 'Mickey Mouse' ); |
835 | |
836 | I := List.IndexOfMin(TTestRecord.NameComparer); |
837 | assert( List[I].Name = 'Anton' ); |
838 | |
839 | // Unique |
840 | List.Add(List[0]); |
841 | List.Insert(2, List[1]); |
842 | List.Insert(4, List[1]); |
843 | List.Unique; |
844 | List.Sort(TTestRecord.NameComparer); |
845 | StrList := List.Convert<string>(TTestRecord.ConvertToNames); |
846 | assert( StrList.Compare(['Anton','Barbie','Jack','Mickey Mouse']) ); |
847 | |
848 | end; |
849 | |
850 | |
851 | |
852 | |
853 | function CompareJokerFunction(const Value: string): boolean; |
854 | begin |
855 | Result := LowerCase(Value) = 'joker'; |
856 | end; |
857 | |
858 | |
859 | procedure TestArrayContainer; |
860 | const |
861 | CWeek: array[1..8] of string = ('Mon','Tues','Wednes','Bug','Thurs','Fri','Satur','Sun'); |
862 | var |
863 | AStr: TArrayRecord<string>; |
864 | AI,AI2: TArrayRecord<integer>; |
865 | I: Integer; |
866 | S: string; |
867 | begin |
868 | AI := TArrayRecord<integer>.Create(0); |
869 | assert(AI.Count = 0); |
870 | AStr := TArrayRecord<string>.Create(10); |
871 | assert((AStr.Count = 10) and (AStr[1] = '')); |
872 | |
873 | // Create |
874 | AI.Create([1,2,3]); |
875 | assert( AI.Compare([1,2,3]) ); |
876 | |
877 | // Add |
878 | AI.Clear; |
879 | assert( AI.Add(1) = 0 ); |
880 | assert( AI.Add(2) = 1 ); |
881 | assert( AI.Add(3) = 2 ); |
882 | |
883 | // IndexOf |
884 | assert( AI.IndexOf(1) = 0 ); |
885 | assert( AI.IndexOf(2) = 1 ); |
886 | assert( AI.IndexOf(5) = -1 ); |
887 | |
888 | // Contains |
889 | assert( AI.Contains(2) = TRUE ); |
890 | assert( AI.Contains(5) = FALSE ); |
891 | assert( AI.Contains(5, TComparer<integer>.Construct( |
892 | function(const Left, Right: integer): Integer |
893 | begin |
894 | Result := (Left + 4) - Right; |
895 | end |
896 | )) = TRUE ); |
897 | |
898 | |
899 | // Delete |
900 | AI.Delete(1); |
901 | assert( AI.Contains(2) = FALSE ); |
902 | assert( AI.Count = 2 ); |
903 | try AI.Delete(2); assert(TRUE); except end; // exception expected |
904 | AI.Delete(0); assert( AI.Count = 1 ); |
905 | AI.Delete(0); assert( AI.Count = 0 ); |
906 | try AI.Delete(0); assert(TRUE); except end; // exception expected |
907 | |
908 | // Insert |
909 | AStr.Clear; |
910 | AStr.Insert(0, 'one'); |
911 | AStr.Insert(0, 'two'); |
912 | assert( AStr.Count = 2 ); |
913 | assert( AStr[0] = 'two' ); |
914 | assert( AStr[1] = 'one' ); |
915 | |
916 | AStr.Insert(2, 'three'); |
917 | assert( (AStr.Count = 3) and (AStr[2] = 'three') ); |
918 | |
919 | // AddRange |
920 | AI.Clear; |
921 | AI.AddRange( TArray<integer>.Create(4,5,6) ); |
922 | assert( (AI.Count = 3) and (AI[2] = 6) ); |
923 | AI.AddRange( TArray<integer>.Create(10,11,12) ); |
924 | assert( (AI.Count = 6) and (AI[5] = 12) and (AI[0] = 4) ); |
925 | |
926 | // Compare |
927 | AI.Create([1,2,3]); |
928 | AI2 := AI; |
929 | Assert( AI.Compare([1,2,3]) ); |
930 | Assert( AI.Compare(AI.Items) ); |
931 | Assert( AI.Compare(AI2) ); |
932 | AI2.Add(4); |
933 | Assert( not AI.Compare(AI2) ); |
934 | |
935 | // Equal |
936 | AI.Create([1,2,3,4,5,6]); |
937 | AI2 := AI; |
938 | assert( AI = AI2 ); |
939 | AI.AddRange( AI2 ); |
940 | assert( (AI.Count = 12) and (AI <> AI2) ); |
941 | AI2.InsertRange( AI2.Count, AI2 ); |
942 | assert( (AI.Count = AI2.Count) and (AI = AI2) ); |
943 | |
944 | // InsertRange |
945 | AI.Clear; |
946 | AI.InsertRange( 0, TArray<integer>.Create(4,5,6) ); |
947 | assert( (AI.Count = 3) and (AI[2] = 6) ); |
948 | AI.InsertRange( 0, [10,11,12]); |
949 | assert( (AI.Count = 6) and (AI[5] = 6) and (AI[0] = 10) ); |
950 | AI.InsertRange( 3,[21,22]); |
951 | assert( (AI.Count = 8) and (AI[7] = 6) and (AI[0] = 10) and (AI[3] = 21) ); |
952 | |
953 | |
954 | // ForEach |
955 | AI.Items := TArray<integer>.Create(5,4,3,2,1); |
956 | AStr.Clear; |
957 | AI.ForEach( |
958 | procedure(var Value: integer; Index: integer) |
959 | begin |
960 | Value := Value * 10; |
961 | AStr.Add(IntToStr(Value)); |
962 | end |
963 | ); |
964 | // sort |
965 | AI.Sort; |
966 | AStr.Sort; |
967 | assert( AI.Compare([10,20,30,40,50]) ); |
968 | assert( AStr.Compare(['10','20','30','40','50']) ); |
969 | |
970 | |
971 | // Find |
972 | AI.Clear; |
973 | AStr.SetItems(['4','king','joker','7','JOKER','joker','ace','joker']); |
974 | I := -1; |
975 | repeat |
976 | I := AStr.Find(CompareJokerFunction, I+1); |
977 | if I >= 0 then AI.Add( I); |
978 | until I < 0; |
979 | assert( AI.Compare([2,4,5,7]) ); |
980 | |
981 | |
982 | // Map |
983 | AI.Clear; |
984 | for I := 1 to 50 do AI.Add( I); |
985 | AI := AI.Map( |
986 | function(var Value: integer; Index: integer): boolean |
987 | begin |
988 | Result := (Value >= 10) and (Value < 20); |
989 | if Result then |
990 | Value := Value + 100; |
991 | end |
992 | ); |
993 | assert( AI.Count = 10 ); |
994 | assert( AI[1] = 111 ); |
995 | |
996 | // Map <string> |
997 | AStr.SetItems(CWeek); |
998 | AStr := AStr.Map( |
999 | function(var Value: string; Index: integer): boolean |
1000 | begin |
1001 | Result := Value <> 'Bug'; |
1002 | Value := Value + 'day'; |
1003 | end |
1004 | ); |
1005 | assert( AStr.Contains('Monday') ); |
1006 | assert( AStr.Contains('Sunday') ); |
1007 | assert( not AStr.Contains('Bugday') ); |
1008 | |
1009 | // enumerate |
1010 | AI.Clear; |
1011 | AStr.SetItems(CWeek); |
1012 | for S in AStr do |
1013 | AI.Add(length(S)); |
1014 | assert( AI.Count = AStr.Count ); |
1015 | assert( AI.Compare([3,4,6,3,5,3,5,3]) ); |
1016 | // check empty enumeration |
1017 | AStr.Clear; |
1018 | for S in AStr do |
1019 | AI.Add(length(S)); |
1020 | assert( AI.Compare([3,4,6,3,5,3,5,3]) ); |
1021 | |
1022 | // Unique |
1023 | AI.Unique; |
1024 | AI.Sort; |
1025 | assert( AI.Compare([3,4,5,6]) ); |
1026 | |
1027 | // CopyArray |
1028 | assert( AI.CopyArray(2).Compare([5,6]) ); |
1029 | assert( AI.CopyArray(0,2).Compare([3,4]) ); |
1030 | assert( AI.CopyArray(1,2).Compare([4,5]) ); |
1031 | |
1032 | |
1033 | end; |
1034 | |
1035 | |
1036 | |
1037 | procedure TestArrayHelper; |
1038 | var |
1039 | AI: TArray<integer>; |
1040 | AStr: TArray<string>; |
1041 | I: Integer; |
1042 | begin |
1043 | // Add |
1044 | AI := NIL; |
1045 | assert( TArray.Add<integer>(AI,1) = 0 ); |
1046 | assert( TArray.Add<integer>(AI,2) = 1 ); |
1047 | assert( TArray.Add<integer>(AI,3) = 2 ); |
1048 | |
1049 | // IndexOf |
1050 | assert( TArray.IndexOf<integer>(AI,1) = 0 ); |
1051 | assert( TArray.IndexOf<integer>(AI,2) = 1 ); |
1052 | assert( TArray.IndexOf<integer>(AI,5) = -1 ); |
1053 | |
1054 | // Contains |
1055 | assert( TArray.Contains<integer>(AI,2) = TRUE ); |
1056 | assert( TArray.Contains<integer>(AI,5) = FALSE ); |
1057 | assert( TArray.Contains<integer>(AI,5, TComparer<integer>.Construct( |
1058 | function(const Left, Right: integer): Integer |
1059 | begin |
1060 | Result := Left - (Right + 4); |
1061 | end |
1062 | )) = FALSE ); |
1063 | |
1064 | |
1065 | // Delete |
1066 | TArray.Delete<integer>(AI,1); |
1067 | assert( TArray.Contains<integer>(AI,2) = FALSE ); |
1068 | assert( length(AI) = 2 ); |
1069 | try TArray.Delete<integer>(AI,2); assert(TRUE); except end; // exception expected |
1070 | TArray.Delete<integer>(AI,0); assert( length(AI) = 1 ); |
1071 | TArray.Delete<integer>(AI,0); assert( length(AI) = 0 ); |
1072 | try TArray.Delete<integer>(AI,0); assert(TRUE); except end; // exception expected |
1073 | |
1074 | // Insert |
1075 | AStr := NIL; |
1076 | TArray.Insert<string>(AStr, 0, 'one'); |
1077 | TArray.Insert<string>(AStr, 0, 'two'); |
1078 | assert( length(AStr) = 2 ); |
1079 | assert( AStr[0] = 'two' ); |
1080 | assert( AStr[1] = 'one' ); |
1081 | |
1082 | TArray.Insert<string>(AStr, 2, 'three'); |
1083 | assert( (length(AStr) = 3) and (AStr[2] = 'three') ); |
1084 | |
1085 | // AddRange |
1086 | AI := NIL; |
1087 | TArray.AddRange<integer>(AI, TArray<integer>.Create(4,5,6)); |
1088 | assert( (length(AI) = 3) and (AI[2] = 6) ); |
1089 | TArray.AddRange<integer>(AI, TArray<integer>.Create(10,11,12)); |
1090 | assert( (length(AI) = 6) and (AI[5] = 12) and (AI[0] = 4) ); |
1091 | |
1092 | // InsertRange |
1093 | AI := NIL; |
1094 | TArray.InsertRange<integer>(AI, 0, TArray<integer>.Create(4,5,6)); |
1095 | assert( (length(AI) = 3) and (AI[2] = 6) ); |
1096 | TArray.InsertRange<integer>(AI, 0, TArray<integer>.Create(10,11,12)); |
1097 | assert( (length(AI) = 6) and (AI[5] = 6) and (AI[0] = 10) ); |
1098 | TArray.InsertRange<integer>(AI, 3, TArray<integer>.Create(21,22)); |
1099 | assert( (length(AI) = 8) and (AI[7] = 6) and (AI[0] = 10) and (AI[3] = 21) ); |
1100 | |
1101 | |
1102 | // ForEach |
1103 | AI := TArray<integer>.Create(5,4,3,2,1); |
1104 | AStr := NIL; |
1105 | TArray.ForEach<integer>( AI, |
1106 | procedure(var Value: integer; Index: integer) |
1107 | begin |
1108 | Value := Value * 10; |
1109 | TArray.Add<string>(AStr, IntToStr(Value)); |
1110 | end |
1111 | ); |
1112 | TArray.Sort<integer>(AI); |
1113 | TArray.Sort<string>(AStr); |
1114 | assert( TArray.Compare<integer>(AI, TArray<integer>.Create(10,20,30,40,50)) ); |
1115 | assert( TArray.Compare<string>(AStr, TArray<string>.Create('10','20','30','40','50')) ); |
1116 | |
1117 | |
1118 | // Find |
1119 | AI := NIL; |
1120 | AStr := TArray<string>.Create('4','king','joker','7','JOKER','joker','ace','joker'); |
1121 | I := -1; |
1122 | repeat |
1123 | I := TArray.Find<string>(AStr, CompareJokerFunction, I+1); |
1124 | if I >= 0 then TArray.Add<integer>(AI, I); |
1125 | until I < 0; |
1126 | assert( TArray.Compare<integer>(AI, TArray<integer>.Create(2,4,5,7)) ); |
1127 | |
1128 | |
1129 | // Map |
1130 | AI := NIL; |
1131 | for I := 1 to 50 do TArray.Add<integer>(AI, I); |
1132 | AI := TArray.Map<integer>(AI, |
1133 | function(var Value: integer; Index: integer): boolean |
1134 | begin |
1135 | Result := (Value >= 10) and (Value < 20); |
1136 | if Result then |
1137 | Value := Value + 100; |
1138 | end |
1139 | ); |
1140 | assert( length(AI) = 10 ); |
1141 | assert( AI[1] = 111 ); |
1142 | |
1143 | // Map <string> |
1144 | AStr := TArray<string>.Create('Mon','Tues','Wednes','Thurs','Fri','Satur','Sun'); |
1145 | AStr := TArray.Map<string>( AStr, |
1146 | function(var Value: string; Index: integer): boolean |
1147 | begin |
1148 | Result := TRUE; |
1149 | Value := Value + 'day'; |
1150 | end |
1151 | ); |
1152 | assert( TArray.Contains<string>(AStr, 'Monday') ); |
1153 | assert( TArray.Contains<string>(AStr, 'Sunday') ); |
1154 | |
1155 | end; |
1156 | |
1157 | class procedure TArrayHelper.Test_All_Helper_Functions; |
1158 | begin |
1159 | TestArrayHelper; |
1160 | TestArrayContainer; |
1161 | Test_TestRecord; |
1162 | end; |
1163 | |
1164 | {$ENDIF TEST_FUNCTION} |
1165 | |
1166 | |
1167 | |
1168 | end. |
1169 | |
1170 |