Difference between revisions of "User:Nhollm"

From Free Pascal wiki
Jump to navigationJump to search
 
(23 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{Warning|All of this is subject to change. Do '''NOT''' use it until its finished. thx}}
+
{{Warning|All of this is subject to change. Do '''NOT''' use it until its finished. For any Questions or Input write me an E-Mail ich@nhollm.de or write me at the forums 'nhollm'.}}
  
==Basic syntax==
+
Todo:
 +
(TObjectDictionary)/ Classes as Values
 +
 
 +
==TDictionary==
 +
TDictionary is a generic Data-Container for Key-value pairs (associative array). <br>
 +
 
 +
It was introduced with Generics.Collections in FPC 3.2.0 (June 2020). It's part of the FPC Runtime Library and fully Delphi compatible.<br>
 +
 
 +
 
 +
==Basic syntax (FPC)==
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 11: Line 20:
  
 
type
 
type
TStringStringDictionary =  specialize TDictionary<string, string>; //specialize a new dictionary and set the data-type of key and value
+
TStrStrDictionary =  specialize TDictionary<string, string>; //specialize a new dictionary-object and set the desired data-type of key and value
  
 
var
 
var
   MyDictionary : TStringStringDictionary;
+
   MyDictionary : TStrStrDictionary;
 
begin
 
begin
   MyDictionary := TStringStringDictionary.Create;
+
   MyDictionary := TStrStrDictionary.Create;
 
   try
 
   try
 
     //do stuff with your dictionary..
 
     //do stuff with your dictionary..
Line 26: Line 35:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Subcategory===
+
==Usage:==
Hello
 
  
==Test==
+
<syntaxhighlight lang="pascal">
 +
</syntaxhighlight>
  
==Test==
+
===Add===
 +
Add Entry or update its current value (key, value)
 +
<syntaxhighlight lang="pascal">
 +
MyDictionary.AddOrSetValue('Key1', 'Value1');
 +
</syntaxhighlight>
  
==Source Code==
+
The function AddOrSetValue is recommended.<br>
 +
Alternatively there is the Function '''Add()'''.<br>
 +
'''Add()''' will throw an error if the Key already exists: 'Dublicate Not Allowed in Dictionary'.<br>
 +
If you only want to add new Items and not overwrite them in the case they exist, take a look at TryAdd() below. It provides a feedback too.
  
 +
===Remove===
 +
<syntaxhighlight lang="pascal">
 +
MyDictionary.Remove('Key2');
 +
</syntaxhighlight>
 +
If the Key does not exist, no Error is thrown.
  
 +
===Clear===
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 +
MyDictionary.Clear;
 +
</syntaxhighlight>
  
{$mode objfpc}
+
===Get Value===
 +
<syntaxhighlight lang="pascal">
 +
WriteLn(MyDictionary.Items['Key1']);
 +
</syntaxhighlight>
 +
If the provided key does not exist, this will throw an Error: 'Dictionary Key does not Exist'. You can use 'TryGetValue' instead.
  
program Example;
+
===Item Count===
 +
Total number of Items in dictionary (count)
 +
<syntaxhighlight lang="pascal">
 +
WriteLn('Items in Dictionary: ' + IntToStr(MyDictionary.Count));
 +
</syntaxhighlight>
  
uses
+
===Loops===
Generics.Collections  //import the generics-library of the RTL
+
<syntaxhighlight lang="pascal">
 +
//Loop Keys
 +
for KeyStr in MyDictionary.Keys do
 +
  WriteLn('Found key:' + KeyStr);
  
type
 
  
TStringStringDictionary =  specialize TDictionary<string, string>; //specialize a new dictionary and set the data-type of key and value
+
//Loop Values
 +
for ValueStr in MyDictionary.Values do
 +
  WriteLn('Found Value: ' + ValueStr);
  
var
 
  MyDictionary : TStringStringDictionary;
 
  TestClass : TmyCoolClass;
 
  //
 
  KeyString: string;
 
  KeyValuePair : TStringStringDictionary.TDictionaryPair;
 
  SearchedValue : string;
 
begin
 
  
  //create the dictionary
+
//Loop Key-Value-Pairs
  MyDictionary := TStringStringDictionary.Create;
+
//var KeyValuePair : TStrStrDictionary.TDictionaryPair;
 +
for KeyValuePair in MyDictionary do
 +
  WriteLn('Found a Pair: Key:' + KeyValuePair.Key + ' Value:' + KeyValuePair.Value);
 +
</syntaxhighlight>
  
  //Add Element or update its value to new (key, value)
+
===Check If Key/Value Exists===
  MyDictionary.AddOrSetValue('Key1', 'Value1');
 
  
  //Remove Element
+
<syntaxhighlight lang="pascal">
  MyDictionary.AddOrSetValue('Key2', 'Another Value');
+
//Check if Key exists
  MyDictionary.Remove('Key2');
+
if MyDictionary.ContainsKey('Key1') then
 +
  WriteLn('key exists')
 +
else
 +
  WriteLn('key not found');
  
  
  //Clear entire dictionary
+
//Check if Value exists
  MyDictionary.Clear;
+
if MyDictionary.ContainsValue('Searched value') then
 +
  writeln('value exists')
 +
else
 +
  writeln('value not found');
 +
</syntaxhighlight>
  
  // Total number of Items in dictionary (count)
+
===Trying===
  writeln('Items in Dictionary: ' + IntToStr(MyDictionary.Count));
+
If you want to get a feedback, use these functions. They return a boolean.<br>
 +
True if sucessfull, False if not. No Error is thrown, so they are a good alternative to GetValue or Add.
  
  //Get Value by Key
+
====TryAdd====
  writeln(MyDictionary.Items['Key1'].Name); //If the provided key does not exist, this will throw a Error :'Dictionary Key does not Exist'
+
<syntaxhighlight lang="pascal">
 +
if MyDictionary.TryAdd('Key','TestValue') then
 +
  writeln('added successfully')
 +
else
 +
  writeln('not sucessfull');
 +
</syntaxhighlight>
 +
TryAdd does not update an existing keys value. Use AddOrSetValue instead. (Even tho there is no feedback provided)
 +
====TryGetValue====
 +
TryGetValue takes two Arguments ->(key, value) and returns a boolean.<br>
 +
If found, it returns '''True''' and the provided 'value' variable becomes the value.<br>
 +
If not found, the function returns '''False''' and the provided 'value'-variable is nil.
  
 +
<syntaxhighlight lang="pascal">
  
  //----------------------Trying------------------
+
if MyDictionary.TryGetValue('Key', SearchedValue) then
 +
  WriteLn('Key found. Its value is: ' + SearchedValue)
 +
else
 +
  WriteLn('Could not get value');
  
 +
//writeln(BoolToStr(MyDictionary.TryGetValue('Key', SearchedValue), true));
 +
</syntaxhighlight>
  
  //TryAdd returns True if added successfully, False otherwise
+
===ToArray===
    writeln('TryAdd: ' + BoolToStr(MyDictionary.TryAdd('Key2',TestClass ),'True', 'False'));
+
Returns an Array of type 'TDictionaryPair'.<br>
 +
<syntaxhighlight lang="pascal">
 +
//var myArray : array of TStrStrDictionary.TDictionaryPair;
 +
myArray := MyDictionary.ToArray()
 +
//WriteLn(length(myArray));
 +
//WriteLn(myArray[0].Key)
 +
//WriteLn(myArray[0].Value)
 +
</syntaxhighlight>
  
  //'TryGetValue'takes two Arguments ->(key, value) and returns a boolean.
+
==Event Notifications==
  //If found, it returns true and the provided 'value' variable becomes the value of the searched KeyValuePair
+
You can set up custom Functions/Procedures which are triggered if an Key or Value got added/removed.<br>
  //if not found,the function returns False and the provided 'value'-variable is nil
+
To do this, we add 2 procedures to the class we specialize from TDictionary.<br>
  writeln(BoolToStr(MyDictionary.TryGetValue('Key', SearchedValue)));
+
The TDictionary class provides the in-build functions 'OnKeyNotify' and 'OnValueNotify'. Now we assign them to our procedures.<br>
  if MyDictionary.TryGetValue('Key', SearchedValue) then
+
(Hint: If you update a existing value, there is no "updated"Event. Instead "removed" followed by "added" is triggered.)
        writeln('Key found. Its Value is ' + SearchedValue.Name )
+
TODO: get the Key Name inside the ValueNotify-Event kontext
  else
 
      if SearchedValue = Nil then
 
          writeln('the provided Value is Nil now');
 
  
 +
<syntaxhighlight lang="pascal" highlight="10,11,41,42">
 +
{$mode objfpc}
 +
program Example;
  
  //----------------------Loop through the Dictionary------------------
+
uses
 +
Generics.Collections;
  
  //Loop Keys
+
type
  for KeyString in MyDictionary.Keys do
+
TStrStrDictionary = Class(Specialize TDictionary<String,String>)
        writeln('Found key:' + KeyString);
+
  private
 +
    procedure DoKeyNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
 +
    procedure DoValueNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
 +
  end;
  
 +
procedure TStrStrDictionary.DoKeyNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
 +
  begin
 +
        //WriteLn(AAction);//AAction can be 'cnRemoved' or 'cnAdded'
 +
        if AAction = cnAdded then
 +
          writeln('A Key got added, the Key is:', AItem)
 +
        else if AAction = cnRemoved then
 +
            writeln('A Key got removed, the Key was:', AItem)
 +
  end;
  
   //Loop Values
+
procedure TStrStrDictionary.DoValueNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
  for TestClass in MyDictionary.Values do
+
   begin
         writeln('Found Value:' + TestClass.Name);
+
        Writeln(AAction);//can be cnRemoved or cnAdded
 +
        if AAction = cnAdded then
 +
          writeln('A Value got added, the value is:', AItem)
 +
         else if AAction = cnRemoved then
 +
          writeln('A Value got removed, the value was:', AItem)
 +
  end;
  
  
  //Loop Key-Value-Pairs
 
  for KeyValuePair in MyDictionary do
 
        writeln('Found a Pair: Key:' + KeyValuePair.Key + ' Value:' + KeyValuePair.Value.Name);
 
  
 +
var
 +
  MyDictionary : TStrStrDictionary;
 +
  TestArr : array of TStrStrDictionary.TDictionaryPair;
 +
begin
 +
  MyDictionary := TStrStrDictionary.Create;
  
 +
  //Event notifications:
 +
  MyDictionary.OnKeyNotify:=@MyDictionary.DoKeyNotify;
 +
  MyDictionary.OnValueNotify:=@MyDictionary.DoValueNotify;
  
     //----------------------Check if.. exists in whole Dictionary------------------
+
  try
 +
     MyDictionary.AddOrSetValue('Key1', 'Value1');//triggers Add Event
 +
    MyDictionary.AddOrSetValue('Key1', 'New Value');//triggers Add and Remove Event
 +
    MyDictionary.Remove('Key1');//triggers Remove Event
 +
    readln();
 +
  finally
 +
    MyDictionary.Free;
 +
  end;
 +
end.
 +
</syntaxhighlight>
  
  //Check if Key exists
 
  if MyDictionary.ContainsKey('Key1') then
 
      writeln('key exists')
 
  else
 
      writeln('key not found');
 
  
 +
==Exceptions==
 +
To handle Exceptions, import the SysUtils Library.
 +
<syntaxhighlight lang="pascal">
 +
{$mode objfpc}
 +
program Example;
 +
...
 +
uses
 +
... SysUtils;
  
  //Check if Value exists
+
try         
   if MyDictionary.ContainsValue(TestClass) then
+
   WriteLn(MyDictionary.Items['KeyXY']); // Accessing a non-existing key
      writeln('value exists')
+
except
  else
 
      writeln('value not found');
 
 
 
  
   readln() //for Windows-console-users
+
   on E: Exception do begin
end.
+
      WriteLn('An exception was raised: ' + E.Message);
 +
      WriteLn(E.ClassName);
 +
  end;
  
 +
end;
  
 +
//Output:
 +
// An exception was raised: Dictionary key does not exist
 +
//EListError
 +
</syntaxhighlight>
  
 +
==Delphi==
 +
<syntaxhighlight lang="pascal">
 +
{$mode delphi}
 +
uses generics.collections;
 +
 +
type  TStrIntDict = TDictionary<string,integer>;
 +
 +
    var
 +
      MyDict : TStrIntDict;
 +
    begin
 +
      MyDict:=TStrIntDict.create;
 +
      MyDict.AddorSetValue('foo',42);
 +
      if MyDict.ContainsKey('foo') then
 +
        WriteLn(MyDict['foo']);
 +
    end.
 
</syntaxhighlight>
 
</syntaxhighlight>

Latest revision as of 14:40, 2 September 2023

Warning-icon.png

Warning: All of this is subject to change. Do NOT use it until its finished. For any Questions or Input write me an E-Mail ich@nhollm.de or write me at the forums 'nhollm'.

Todo: (TObjectDictionary)/ Classes as Values

TDictionary

TDictionary is a generic Data-Container for Key-value pairs (associative array).

It was introduced with Generics.Collections in FPC 3.2.0 (June 2020). It's part of the FPC Runtime Library and fully Delphi compatible.


Basic syntax (FPC)

{$mode objfpc}
program Example;

uses
Generics.Collections;  //import the generics-library of the RTL

type
TStrStrDictionary =  specialize TDictionary<string, string>; //specialize a new dictionary-object and set the desired data-type of key and value

var
  MyDictionary : TStrStrDictionary;
begin
  MyDictionary := TStrStrDictionary.Create;
  try
    //do stuff with your dictionary..
  finally
    MyDictionary.Free;
  end;
end.

Usage:

Add

Add Entry or update its current value (key, value)

MyDictionary.AddOrSetValue('Key1', 'Value1');

The function AddOrSetValue is recommended.
Alternatively there is the Function Add().
Add() will throw an error if the Key already exists: 'Dublicate Not Allowed in Dictionary'.
If you only want to add new Items and not overwrite them in the case they exist, take a look at TryAdd() below. It provides a feedback too.

Remove

MyDictionary.Remove('Key2');

If the Key does not exist, no Error is thrown.

Clear

MyDictionary.Clear;

Get Value

WriteLn(MyDictionary.Items['Key1']);

If the provided key does not exist, this will throw an Error: 'Dictionary Key does not Exist'. You can use 'TryGetValue' instead.

Item Count

Total number of Items in dictionary (count)

WriteLn('Items in Dictionary: ' + IntToStr(MyDictionary.Count));

Loops

//Loop Keys
for KeyStr in MyDictionary.Keys do
  WriteLn('Found key:' + KeyStr);


//Loop Values
for ValueStr in MyDictionary.Values do
  WriteLn('Found Value: ' + ValueStr);


//Loop Key-Value-Pairs
//var KeyValuePair : TStrStrDictionary.TDictionaryPair;
for KeyValuePair in MyDictionary do
  WriteLn('Found a Pair: Key:' + KeyValuePair.Key + ' Value:' + KeyValuePair.Value);

Check If Key/Value Exists

//Check if Key exists
if MyDictionary.ContainsKey('Key1') then
  WriteLn('key exists')
else
  WriteLn('key not found');


//Check if Value exists
if MyDictionary.ContainsValue('Searched value') then
  writeln('value exists')
else
  writeln('value not found');

Trying

If you want to get a feedback, use these functions. They return a boolean.
True if sucessfull, False if not. No Error is thrown, so they are a good alternative to GetValue or Add.

TryAdd

if MyDictionary.TryAdd('Key','TestValue') then
  writeln('added successfully')
else
  writeln('not sucessfull');

TryAdd does not update an existing keys value. Use AddOrSetValue instead. (Even tho there is no feedback provided)

TryGetValue

TryGetValue takes two Arguments ->(key, value) and returns a boolean.
If found, it returns True and the provided 'value' variable becomes the value.
If not found, the function returns False and the provided 'value'-variable is nil.

if MyDictionary.TryGetValue('Key', SearchedValue) then
  WriteLn('Key found. Its value is: ' + SearchedValue)
else
  WriteLn('Could not get value');

//writeln(BoolToStr(MyDictionary.TryGetValue('Key', SearchedValue), true));

ToArray

Returns an Array of type 'TDictionaryPair'.

//var myArray :  array of TStrStrDictionary.TDictionaryPair;
myArray := MyDictionary.ToArray()
//WriteLn(length(myArray));
//WriteLn(myArray[0].Key)
//WriteLn(myArray[0].Value)

Event Notifications

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 in-build functions 'OnKeyNotify' and 'OnValueNotify'. Now we assign them to our procedures.
(Hint: If you update a existing value, there is no "updated"Event. Instead "removed" followed by "added" is triggered.) TODO: get the Key Name inside the ValueNotify-Event kontext

{$mode objfpc}
program Example;

uses
Generics.Collections;

type
TStrStrDictionary = Class(Specialize TDictionary<String,String>)
  private
    procedure DoKeyNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
    procedure DoValueNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
  end;

procedure TStrStrDictionary.DoKeyNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
   begin
        //WriteLn(AAction);//AAction can be 'cnRemoved' or 'cnAdded'
        if AAction = cnAdded then
           writeln('A Key got added, the Key is:', AItem)
        else if AAction = cnRemoved then
            writeln('A Key got removed, the Key was:', AItem)
   end;

procedure TStrStrDictionary.DoValueNotify(ASender: TObject;constref AItem: ShortString; AAction: TCollectionNotification);
   begin
        Writeln(AAction);//can be cnRemoved or cnAdded
        if AAction = cnAdded then
          writeln('A Value got added, the value is:', AItem)
        else if AAction = cnRemoved then
          writeln('A Value got removed, the value was:', AItem)
   end;



var
  MyDictionary : TStrStrDictionary;
  TestArr : array of TStrStrDictionary.TDictionaryPair;
begin
  MyDictionary := TStrStrDictionary.Create;

  //Event notifications:
  MyDictionary.OnKeyNotify:=@MyDictionary.DoKeyNotify;
  MyDictionary.OnValueNotify:=@MyDictionary.DoValueNotify;

  try
    MyDictionary.AddOrSetValue('Key1', 'Value1');//triggers Add Event
    MyDictionary.AddOrSetValue('Key1', 'New Value');//triggers Add and Remove Event
    MyDictionary.Remove('Key1');//triggers Remove Event
    readln();
  finally
    MyDictionary.Free;
  end;
end.


Exceptions

To handle Exceptions, import the SysUtils Library.

{$mode objfpc}
program Example;
...
uses
... SysUtils;

try          
   WriteLn(MyDictionary.Items['KeyXY']); // Accessing a non-existing key 
except

   on E: Exception do begin
      WriteLn('An exception was raised: ' + E.Message);
      WriteLn(E.ClassName);
   end;

end;

//Output:
// An exception was raised: Dictionary key does not exist
//EListError

Delphi

{$mode delphi}
uses generics.collections;
 
type  TStrIntDict = TDictionary<string,integer>;
 
    var
      MyDict : TStrIntDict;
    begin
      MyDict:=TStrIntDict.create;
      MyDict.AddorSetValue('foo',42);
      if MyDict.ContainsKey('foo') then
        WriteLn(MyDict['foo']);
    end.