Delphi Generics> Dictionary with default value

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

    //
    // Dictionary with default response
    //
    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

//
// Contructor and destructor
//
constructor TDefaultDictonary<K, V>.Create(Defaultvalue : V);
begin
    inherited Create;

    M_DefaultValue := Defaultvalue;
end;

destructor TDefaultDictonary<K, V>.Destroy;
begin
    inherited Destroy;
end;

//
// Get the default Value
//
function TDefaultDictonary<K, V>.GetDefaultValue : V;
begin
    Result := M_DefaultValue;
end;


//
// Try to get a value from the dictionary for the given key.
//
// If the value is found then "Value" holds it and the function returns true.
// If the value is not found then "Value" holds the default value and the
// function returns false.
//
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;

    // Outputs:
    Value := DictVal;
    Result := IsKeyFound;
end;


//
// Get a value from the dictionary for the given key.
//
// If the value is found then the function returns it.
// If the value is not found the function returns the default value.
//
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');

    //
    // It is possible to oveload the indexer operator?
    // Please review or delete me !
    //
    //checkEquals('Default response', dd[2],
    //        '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

+5
source share
2 answers

Before you write all this code yourself, you can look at the general classes in the DeHL library .

He supports this:

, Delphi ( ). " " .

-

+1

: GetItem TDictionary < > . ,

 property Items[const Key: K]: V read GetValueOf write SetItem; default;

.

Btw, , , , , , . , Items [] , , .

.

0

All Articles