Sign in Join
Wiki copytemporary

TDictionary: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: TDictionary

OldNewLine
1+## TDictionary
2+
3+TDictionary is a generic data-container for key-value pairs (associative array).
4+
5+It was introduced with Generics.Collections (rtl-generics) in FPC 3.1.1+ and published in major release 3.2 (June 2020). It's part of the FPC Runtime Library and fully Delphi compatible.
6+
7+(Like in Delphi) The TDictionary is implemented as a hash table. This means the key-value pairs are not sorted in a defined order in memory and are not iterable by index. However you can iterate over the Key- and Value- collections. Or you can turn them in an array and iterate over that.
8+
9+Both key and value can be of primitive type (e.g. integer, string, boolean) or rather complex (e.g. classes, records, arrays).
10+
11+It is recommended to name the specialized dictionary-type with abbreviations of key and value types e.g "TStringIntegerDict" when the key is a of type string and the value is of type integer.
12+
13+## Basic example
14+```
15+{$mode objfpc} {$H+}
16+ program Example;
17+
18+ uses
19+ Generics.Collections;
20+
21+ type
22+ TStrStrDictionary = specialize TDictionary<string, string>;
23+
24+ var
25+ MyDictionary: TStrStrDictionary;
26+ begin
27+ MyDictionary := TStrStrDictionary.Create;
28+ try
29+ //do stuff with your dictionary..
30+ MyDictionary.AddOrSetValue('Key1', 'Value1');
31+
32+ if MyDictionary.ContainsKey('Key1') then
33+ WriteLn(MyDictionary['Key1']);
34+
35+ finally
36+ MyDictionary.Free;
37+ end;
38+ end.
39+```
40+
41+See also #Example in Delphi mode.
42+
43+## Usage:
44+
45+### Add
46+
47+**AddOrSetValue()** is recommended, because **Add()** will fail, if the key already exists: 'Dublicate not allowed in Dictionary'.
48+
49+#### Add
50+```
51+MyDictionary.Add('Key1', 'Value1');
52+```
53+
54+#### AddOrSetValue
55+
56+recommended
57+```
58+MyDictionary.AddOrSetValue('Key1', 'Value1');
59+```
60+
61+### Remove
62+```
63+MyDictionary.Remove('Key2');
64+```
65+
66+If the Key does not exist, no Error is thrown.
67+
68+### Clear
69+
70+Remove all entries.
71+```
72+MyDictionary.Clear;
73+```
74+
75+### Get Value
76+```
77+WriteLn(MyDictionary['Key1']);
78+
79+ //WriteLn(MyDictionary.Items['Key1']); works aswell
80+```
81+
82+If the provided key does not exist, this will throw an Error: 'Dictionary Key does not Exist'. You could check with .ContainsKey() beforehand or use TryGetValue().
83+
84+### Item Count
85+
86+Total number of Items in dictionary (count)
87+```
88+WriteLn('Items in Dictionary: ', MyDictionary.Count);
89+```
90+
91+### Loops
92+
93+#### Loop Keys
94+```
95+for KeyStr in MyDictionary.Keys do
96+ WriteLn('Found key:', KeyStr);
97+```
98+
99+#### Loop Values
100+```
101+for ValueStr in MyDictionary.Values do
102+ WriteLn('Found value: ', ValueStr);
103+```
104+
105+#### Loop Key-Value-Pairs
106+```
107+//var KeyValuePair : TStrStrDictionary.TDictionaryPair;
108+ for KeyValuePair in MyDictionary do
109+ WriteLn('Found a Pair: Key:' , KeyValuePair.Key , ' Value:' , KeyValuePair.Value);
110+```
111+
112+### Check If Key/Value Exists
113+```
114+//Check if Key exists
115+ if MyDictionary.ContainsKey('Key1') then
116+ WriteLn('key exists')
117+ else
118+ WriteLn('key not found');
119+
120+
121+ //Check if Value exists
122+ if MyDictionary.ContainsValue('Searched value') then
123+ writeln('value exists')
124+ else
125+ writeln('value not found');
126+```
127+
128+### Trying
129+
130+These functions return a boolean.
131+True if sucessfull, False if not. No error is thrown, so they are a good alternative to Get Value or Add().
132+
133+#### TryAdd
134+```
135+if MyDictionary.TryAdd('Key','TestValue') then
136+ writeln('added successfully')
137+ else
138+ writeln('not successfull');
139+```
140+
141+Limitation: TryAdd does **not** update an existing keys value. Use AddOrSetValue() instead. (Even tho there is no feedback provided)
142+
143+#### TryGetValue
144+
145+TryGetValue takes two Arguments ->(key, value) and returns a boolean.
146+If found, it returns **True** and the provided 'value' variable becomes the value.
147+If not found, the function returns **False** and the provided 'value'-variable is **nil**.
148+```
149+//var SearchedValue: string;
150+ if MyDictionary.TryGetValue('Key 1', SearchedValue) then
151+ WriteLn('Key found. Its value is: ' + SearchedValue)
152+ else
153+ WriteLn('Could not get value');
154+
155+ //writeln(BoolToStr(MyDictionary.TryGetValue('Key 1', SearchedValue), true));
156+```
157+
158+### ToArray
159+
160+ToArray method returns an array of type TDictionaryPair:
161+```
162+var
163+ myArray: array of TStrStrDictionary.TDictionaryPair;
164+ begin
165+ myArray := MyDictionary.ToArray()
166+ WriteLn(length(myArray));
167+ WriteLn(myArray[0].Key)
168+ WriteLn(myArray[0].Value)
169+```
170+
171+
172+
173+Get an array of all keys, i.e. turn the Keys collection into an array:
174+```
175+var
176+ keyArray: array of string; //needs to be the correct type
177+ i: integer;
178+ begin
179+ keyArray := MyDictionary.Keys.ToArray()
180+ WriteLn(length(keyArray));
181+ WriteLn(keyArray[0])
182+ for i := 0 to Length(keyArray)-1 do
183+ writeln(keyArray[i]);
184+```
185+
186+Get an array of all values, i.e. turn the Values collection into an array:
187+```
188+var
189+ valueArray: array of string; //needs to be the correct type
190+ i: integer;
191+ begin
192+ valueArray := MyDictionary.Values.ToArray()
193+ WriteLn(length(valueArray));
194+ WriteLn(valueArray[0])
195+ for i := 0 to Length(valueArray)-1 do
196+ writeln(valueArray[i]);
197+```
198+
199+## Event notifications
200+
201+You can set up custom functions/procedures which are triggered if an Key or Value got added/removed. To do this, we add 2 procedures to the class we specialize from TDictionary. The TDictionary class provides the events OnKeyNotify and OnValueNotify. We assign them to our procedures.
202+
203+Hint: If you update a existing value, there is no "updated" event. Instead "removed" followed by "added" is triggered.
204+
205+TODO: get the Key name inside the ValueNotify event context.
206+```
207+{$mode objfpc} {$H+}
208+ program Example;
209+
210+ uses
211+ Generics.Collections;
212+
213+ type
214+ TStrStrDictionary = class(specialize TDictionary<ShortString, ShortString>)
215+ private
216+ procedure DoKeyNotify(ASender: TObject; constref AItem: ShortString; AAction: TCollectionNotification);
217+ procedure DoValueNotify(ASender: TObject; constref AItem: ShortString; AAction: TCollectionNotification);
218+ end;
219+
220+ procedure TStrStrDictionary.DoKeyNotify(ASender: TObject; constref AItem: ShortString; AAction: TCollectionNotification);
221+ begin
222+ //WriteLn(AAction);
223+ //AAction can be cnRemoved or cnAdded
224+ if AAction = cnAdded then
225+ writeln('A Key got added, the Key is:', AItem)
226+ else if AAction = cnRemoved then
227+ writeln('A Key got removed, the Key was:', AItem)
228+ end;
229+
230+ procedure TStrStrDictionary.DoValueNotify(ASender: TObject; constref AItem: ShortString; AAction: TCollectionNotification);
231+ begin
232+ //how can we get here the key this value was added to?
233+
234+ //Writeln(AAction);
235+ //can be cnRemoved or cnAdded
236+ if AAction = cnAdded then
237+ writeln('A Value got added, the value is:', AItem)
238+ else if AAction = cnRemoved then
239+ writeln('A Value got removed, the value was:', AItem)
240+ end;
241+
242+
243+ var
244+ MyDictionary: TStrStrDictionary;
245+ TestArr: array of TStrStrDictionary.TDictionaryPair;
246+ begin
247+ MyDictionary := TStrStrDictionary.Create;
248+
249+ //Event notifications:
250+ MyDictionary.OnKeyNotify := @MyDictionary.DoKeyNotify;
251+ MyDictionary.OnValueNotify := @MyDictionary.DoValueNotify;
252+
253+ try
254+ MyDictionary.AddOrSetValue('Key1', 'Value1'); //triggers 'Add' Event
255+ MyDictionary.AddOrSetValue('Key1', 'New Value'); //triggers 'Remove' and 'Add' Event
256+ MyDictionary.Remove('Key1'); //triggers 'Remove' Event
257+ Readln();
258+ finally
259+ MyDictionary.Free;
260+ end;
261+ end.
262+```
263+
264+## Using Classes
265+
266+You can use classes for Keys and/or Values. Because of the fact that all created classes must be manually freed, it is strongly recommended to use a [[TObjectDictionary]] instead.
267+A [[TObjectDictionary]] takes ownership of its contained classes and frees them automaticly if its freed.
268+However here is an example where manual freeing is required:
269+```
270+{$mode objfpc} {H+}
271+ program example;
272+
273+ uses
274+ Generics.Collections;
275+
276+ type
277+ TMyTestClass = class
278+ Name: string;
279+ end;
280+
281+ TIntClassDictionary = specialize TDictionary<Integer, TMyTestClass>;
282+
283+ var
284+ MyDictionary: TIntClassDictionary;
285+ MyClass: TMyTestClass;
286+ i: integer;
287+ begin
288+ MyDictionary := TIntClassDictionary.Create;
289+ try
290+ for i:= 0 to 5 do
291+ begin
292+ MyClass := TMyTestClass.Create;
293+ MyClass.Name := 'Something';
294+ MyDictionary.Add(i, MyClass);
295+ end;
296+
297+ //...
298+
299+ finally
300+ //manual freeing
301+ for MyClass in MyDictionary.Values do
302+ MyClass.Free;
303+
304+ MyDictionary.Free;
305+ end;
306+ end.
307+```
308+
309+## Exceptions
310+
311+To handle exceptions, import the SysUtils unit.
312+```
313+{$mode objfpc} {H+}
314+ program Example;
315+
316+ uses
317+ ..., SysUtils;
318+
319+ try
320+ WriteLn(MyDictionary.Items['KeyXY']); // Accessing a non-existent key
321+ except
322+ on E: Exception do begin
323+ WriteLn('An exception was raised: ', E.Message);
324+ WriteLn(E.ClassName);
325+ end;
326+ end;
327+
328+ //Output:
329+ // An exception was raised: Dictionary key does not exist
330+ //EListError
331+```
332+
333+## Example in Delphi mode
334+```
335+{$mode delphi}
336+ uses generics.collections;
337+
338+ type
339+ TStrIntDict = TDictionary<string, integer>;
340+
341+ var
342+ MyDict: TStrIntDict;
343+ begin
344+ MyDict := TStrIntDict.create;
345+ MyDict.AddOrSetValue('foo', 42);
346+ if MyDict.ContainsKey('foo') then
347+ WriteLn(MyDict['foo']);
348+ end.
349+```
350+
351+---
352+*From the Free Pascal wiki, page "TDictionary", as the Internet Archive kept it on 2025-02-14, last changed on 2025-01-06. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history