1// Generic types for FreeSparta.com and FreePascal!
2// Original version by keeper89.blogspot.com, 2011
3// FPC version by Maciej Izak (hnb), 2014
4
5program TObjectListProject;
6
7{$MODE DELPHI}
8{$APPTYPE CONSOLE}
9
10uses
11  SysUtils, Generics.Collections, Generics.Defaults, DateUtils;
12
13type
14  TPlayer = class
15  public
16    Name, Team: string;
17    BirthDay: TDateTime;
18    NTeamGoals: Byte; // Number of goals for the national team
19    constructor Create(const Name: string; BirthDay: TDateTime;
20      const Team: string; NTeamGoals: Byte = 0);
21    function ToString: string;
22  end;
23
24  // Class containing handlers add / remove list items
25  TListEventsHandler = class
26  public
27    class procedure OnListChanged(Sender: TObject; constref Item: TPlayer;
28      Action: TCollectionNotification);
29  end;
30
31
32constructor TPlayer.Create(const Name: string; BirthDay: TDateTime;
33  const Team: string; NTeamGoals: Byte);
34begin
35  Self.Name := Name;
36  Self.BirthDay := BirthDay;
37  Self.Team := Team;
38  Self.NTeamGoals := NTeamGoals;
39end;
40
41function TPlayer.ToString: string;
42begin
43  Result := Format('%s - Age: %d Team: %s Goals: %d',
44                   [Name,
45                    DateUtils.YearsBetween(Date, BirthDay),
46                    Team, NTeamGoals])
47end;
48
49// Function sort descending goals for the national team
50function ComparePlayersByGoalsDecs(constref Player1, Player2: TPlayer): Integer;
51begin
52  Result := TCompare.UInt8(Player2.NTeamGoals, Player1.NTeamGoals);
53end;
54
55class procedure TListEventsHandler.OnListChanged(Sender: TObject; constref Item: TPlayer;
56  Action: TCollectionNotification);
57var
58  Mes: string;
59begin
60  // Unlike TDictionary we added Action = cnExtracted
61  case Action of
62    cnAdded:
63      Mes := 'added to the list!';
64    cnRemoved:
65      Mes := 'removed from the list!';
66    cnExtracted:
67      Mes := 'extracted from the list!';
68  end;
69  Writeln(Format('Football player %s %s ', [Item.ToString, Mes]));
70end;
71
72var
73  // Declare TObjectList as storage for TPlayer
74  PlayersList: TObjectList<TPlayer>;
75  Player: TPlayer;
76  FoundIndex: PtrInt;
77begin
78  WriteLn('Working with TObjectList - football manager');
79  WriteLn;
80
81  PlayersList := TObjectList<TPlayer>.Create;
82
83  // ---------------------------------------------------
84  // 1) Adding items
85
86  PlayersList.Add(
87    TPlayer.Create('Zinedine Zidane', EncodeDate(1972, 06, 23), 'France', 31));
88  PlayersList.Add(
89    TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44));
90  PlayersList.Add(
91    TPlayer.Create('Ronaldo', EncodeDate(1976, 09, 22), 'Brazil', 62));
92   // Adding the specified position
93  PlayersList.Insert(0,
94    TPlayer.Create('Luis Figo', EncodeDate(1972, 11, 4), 'Portugal', 33));
95  // Add a few players through InsertRange (AddRange works similarly)
96  PlayersList.InsertRange(0,
97    [TPlayer.Create('David Beckham', EncodeDate(1975, 05, 2), 'England', 17),
98     TPlayer.Create('Alessandro Del Piero', EncodeDate(1974, 11, 9), 'Italy ', 27),
99     TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44)]);
100  Player := TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44);
101  PlayersList.Add(Player);
102
103
104  // ---------------------------------------------------
105  // 2) Access and check the items
106
107  // Is there a player in the list - Contains
108  if PlayersList.Contains(Player) then
109    Writeln('Raul is in the list!');
110  // Player index and count of items in the list
111  Writeln(Format('Raul is %d-th on the list of %d players.',
112                 [PlayersList.IndexOf(Player) + 1, PlayersList.Count]));
113  // Index access
114  Writeln(Format('1st in the list: %s', [PlayersList[0].ToString]));
115  // The first player
116  Writeln(Format('1st in the list: %s', [PlayersList.First.ToString]));
117  // The last player
118  Writeln(Format('Last in the list: %s', [PlayersList.Last.ToString]));
119  // "Reverse" elements
120  PlayersList.Reverse;
121  Writeln('List items have been "reversed"');
122  Writeln;
123
124
125  // ---------------------------------------------------
126  // 3) Moving and removing items
127
128  // Changing places players in the list
129  PlayersList.Exchange(0, 1);
130  // Move back 1 player
131  PlayersList.Move(1, 0);
132
133  // Removes the element at index
134  PlayersList.Delete(5);
135  // Or a number of elements starting at index
136  PlayersList.DeleteRange(5, 2);
137  // Remove the item from the list, if the item
138  // exists returns its index in the list
139  Writeln(Format('Removed %d-st player', [PlayersList.Remove(Player) + 1]));
140
141  // Extract and return the item, if there is no Player in the list then
142  // Extract will return = nil, (anyway Raul is already removed via Remove)
143  Player := PlayersList.Extract(Player);
144  if Assigned(Player) then
145    Writeln(Format('Extracted: %s', [Player.ToString]));
146
147  // Clear the list completely
148  PlayersList.Clear;
149  Writeln;
150
151  // ---------------------------------------------------
152  // 4) Event OnNotify, sorting and searching
153
154  PlayersList.OnNotify := TListEventsHandler.OnListChanged;
155
156  PlayersList.Add(
157    TPlayer.Create('Zinedine Zidane', EncodeDate(1972, 06, 23), 'France', 31));
158  PlayersList.Add(
159    TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44));
160  PlayersList.Add(
161    TPlayer.Create('Ronaldo', EncodeDate(1976, 09, 22), 'Brazil', 62));
162  PlayersList.AddRange(
163    [TPlayer.Create('David Beckham', EncodeDate(1975, 05, 2), 'England', 17),
164     TPlayer.Create('Alessandro Del Piero', EncodeDate(1974, 11, 9), 'Italy ', 27),
165     TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44)]);
166
167  PlayersList.Remove(PlayersList.Last);
168  Player := PlayersList.Extract(PlayersList[0]);
169
170  PlayersList.Sort(TComparer<TPlayer>.Construct(ComparePlayersByGoalsDecs));
171  Writeln;
172  Writeln('Sorted list of players:');
173  for Player in PlayersList do
174    Writeln(Player.ToString);
175  Writeln;
176
177  // Find Ronaldo!
178  // TArray BinarySearch requires sorted list
179  // IndexOf does not require sorted list
180  // but BinarySearch is usually faster
181  Player := PlayersList[0];
182  if PlayersList.BinarySearch(Player, FoundIndex,
183    TComparer<TPlayer>.Construct(ComparePlayersByGoalsDecs)) then
184    Writeln(Format('Ronaldo is in the sorted list at position %d', [FoundIndex + 1]));
185
186  Writeln;
187
188  // With the destruction of the list remove all elements
189  // OnNotify show it
190  FreeAndNil(PlayersList);
191
192  Readln;
193end.
194
195