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 covered219
Number of lines with code gen267
Line coverage82%


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