I would like to have a dictionary that returns the default value if the search key is not found. Reading from the documentation:
Generics.Collections.Tdictionary [...] This class provides mapping [...] and initial content.
1 - How? Is there any way to do this ala Python: {1: 'one; 2: two}?
Generics.Collections.TDictionary.TryGetValue [...] TryGetValue returns true if the given key is in the dictionary and provides its value in Value. Otherwise, it returns false, and the value is set to the default value for the Tvalue value.
2 - How to set the default value? I cannot find the constructor (maybe I just searched in the wrong place. I expect something like "Create constructor (DefaultValue: TValue);")
So, I'm trying to implement my own (maybe not necessary. See above):
Code (feedback and suggestions are welcome!):
unit Util;
interface
uses
Generics.collections;
type
TDefaultDictonary<K, V> = class(TObjectDictionary<K, V>)
private
M_DefaultValue : V;
public
constructor Create(Defaultvalue : V);
destructor Destroy; reintroduce;
function GetDefaultValue : V;
function TryGetValue(const Key: K; out Value: V): Boolean;
function GetValueOf(const Key: K) : V;
end;
implementation
constructor TDefaultDictonary<K, V>.Create(Defaultvalue : V);
begin
inherited Create;
M_DefaultValue := Defaultvalue;
end;
destructor TDefaultDictonary<K, V>.Destroy;
begin
inherited Destroy;
end;
function TDefaultDictonary<K, V>.GetDefaultValue : V;
begin
Result := M_DefaultValue;
end;
function TDefaultDictonary<K, V>.TryGetValue(const Key: K; out Value: V): Boolean;
var
IsKeyFound : boolean;
DictVal : V;
begin
IsKeyFound := inherited TryGetValue(Key, DictVal);
if not IsKeyFound then begin
DictVal := M_DefaultValue;
end;
Value := DictVal;
Result := IsKeyFound;
end;
function TDefaultDictonary<K, V>.GetValueOf(const Key: K) : V;
var
DictVal : V;
begin
TryGetValue(Key, DictVal);
Result := DictVal;
end;
And tests:
unit Test_Utils;
{
Test the TDefaultDictionary functionality
}
interface
uses
Sysutils, Math, TestFramework, Util;
type
TestUtil = class(TTestCase)
public
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestDefaultDictionaryGetDefaultResponse;
procedure TestDefaultDictionaryExistingKey;
procedure TestDefaultDictionaryNotExistingKey;
end;
implementation
procedure TestUtil.SetUp;
begin
end;
procedure TestUtil.TearDown;
begin
end;
procedure TestUtil.TestDefaultDictionaryGetDefaultResponse;
var
dd : TDefaultDictonary<integer, string>;
begin
dd := TDefaultDictonary<integer, string>.Create('Default response');
checkEquals('Default response', dd.GetDefaultValue);
dd.Free;
end;
procedure TestUtil.TestDefaultDictionaryExistingKey;
var
dd : TDefaultDictonary<integer, string>;
outVal : string;
isKeyFound : boolean;
begin
dd := TDefaultDictonary<integer, string>.Create('Default response');
dd.Add(1, 'My one');
checkEquals(1, dd.Count,
'One element as count');
isKeyFound := dd.TryGetValue(1, outVal);
check(isKeyFound,
'Key not found by TryGetValue');
checkEquals('My one', outVal,
'Value given by TryGetValue');
checkEquals('My one', dd[1],
'Value given by indexing as array');
dd.Free;
end;
procedure TestUtil.TestDefaultDictionaryNotExistingKey;
var
dd : TDefaultDictonary<integer, string>;
outVal : string;
isKeyFound : boolean;
begin
dd := TDefaultDictonary<integer, string>.Create('Default response');
dd.Add(1, 'one');
isKeyFound := dd.TryGetValue(2, outVal);
check(not isKeyFound,
'Key should not be found by TryGetValue');
checkEquals('Default response', outVal,
'Default Value given by TryGetValue');
checkEquals('Default response', dd.GetValueOf(2),
'Default Value given by indexing as array');
dd.Free;
end;
initialization
RegisterTest(TestUtil.Suite);
end.
This is far from complete. I would also like the indexer operator to work (see Last Test). Is it possible? What should be implemented?
Is this implementation a leak of M_DefaultValue (I'm new to Delphi). I cannot do something M_DefaultValue.Free in the destructor (not so flexible due to constructor constraint) What can I do here?
Thanks in advance,
Francis