TDictionary is populated during the creation of the sample code

Does anyone have some sample code TDictionary<TKey, TValue>

that gets populated during its constructor?

+3


source to share


3 answers


You need to call an overload of the dictionary constructor that takes a Collection

type parameter TEnumerable<TPair<TKey, TValue>>

.

For example, suppose we have TDictionary<string, Integer>

. Then we could pass an instance to the constructor TEnumerable<TPair<string, Integer>>

. An example of such a thing is TList<TPair<string, Integer>>

.

List := TList<TPair<string, Integer>>.Create;
List.Add(TPair<string, Integer>.Create('Foo', 42));
List.Add(TPair<string, Integer>.Create('Bar', 666));
Dictionary := TDictionary<string, Integer>.Create(List);

      

This is very cumbersome and you would never prefer this option over a simple Create

one and then a series of calls Add

. You would only use the option of transferring to an existing collection if you ended up with a ready-made one at hand.



Another example of a class that is derived from TEnumerable<T>

is TDictionary

:

type
  TDictionary<TKey,TValue> = class(TEnumerable<TPair<TKey,TValue>>)

      

So, if you already have one instance of the dictionary, you can create another one and initialize it with the contents of the first one:

Dict2 := TDictionary<string, Integer>.Create(Dict1);

      

+3


source


Apparently you just need a one-liner, so I gave it a try, implemented TDictHelper

that allows you to create and populate a dictionary using a one-liner.


The problem with initializing a dictionary using any form of one-linear is that it needs value pairs and we don't have the necessary syntax to pass those pairs. For example, if you wanted to use the syntax TPair<Key, Value>.Create(A, B)

for every pair of values ​​added to the dictionary, that would be one ugly one liner.

I figured out a couple of good alternatives; The first is used as follows:

  with TDictHelper<Integer, string> do
    Dict := Make([P(1, 'one'), P(2, 'two')]);

      

Usage with

is required because the TDictHelper

class I implemented in the class has a procedure Make

that takes an array as a parameter TPair<Key, Value>

; It would be unusable if I wrote it like:

Dict := TDictHelper<Integer, string>.Make(TPair<Integer, string>.Create(1, 'one'), TPair<Integer, string>.Create(2, 'two'));

      



It would work, but it would be very, very ugly!

Since usage with

can be problematic (especially if you want to use two dictionaries), I have included an alternative syntax; Unfortunately this one doesn't scale, it gets real ugly:

  Dict := TDictHelper<Integer, string>.Make([1, 2], ['one', 'two']);

      

This alternative takes two separate arrays for keys and values, concatenates them inside a method Make

. Looks fine for 2-3 items, but doesn't scale: what if you have 10 items and need to remove the 7th pair? You will need to COUNT the items and that is error prone.

Here's the complete code, not a lot:

program Project25;

{$APPTYPE CONSOLE}

uses
  SysUtils, Generics.Collections;

type
  TDictHelper<Key, Value> = class
  public
    class function P(const K:Key; const V:Value): TPair<Key, Value>;
    class function Make(init: array of TPair<Key, Value>): TDictionary<Key, Value>;overload;
    class function Make(KeyArray: array of Key; ValueArray: array of Value): TDictionary<Key, Value>;overload;
  end;

{ TDictHelper<Key, Value> }

class function TDictHelper<Key, Value>.Make(init: array of TPair<Key, Value>): TDictionary<Key, Value>;
var P: TPair<Key, Value>;
begin
  Result := TDictionary<Key, Value>.Create;
  for P in init do
    Result.AddOrSetValue(P.Key, P.Value);
end;

class function TDictHelper<Key, Value>.Make(KeyArray: array of Key;
  ValueArray: array of Value): TDictionary<Key, Value>;
var i:Integer;
begin
  if Length(KeyArray) <> Length(ValueArray) then
    raise Exception.Create('Number of keys does not match number of values.');
  Result := TDictionary<Key, Value>.Create;
  for i:=0 to High(KeyArray) do
    Result.AddOrSetValue(KeyArray[i], ValueArray[i]);
end;

class function TDictHelper<Key, Value>.P(const K: Key;
  const V: Value): TPair<Key, Value>;
begin
  Result := TPair<Key, Value>.Create(K, V);
end;

// ============================== TEST CODE FOLLOWS

var Dict: TDictionary<Integer, string>;
    Pair: TPair<Integer, string>;

begin
  try
    try
      // Nice-looking but requires "with" and you can't work with two kinds of DictHelper at once
      with TDictHelper<Integer, string> do
        Dict := Make([P(1, 'one'), P(2, 'two')]);
      // Use the array
      for Pair in Dict do
        WriteLn(Pair.Key, ' = ', Pair.Value);
      Dict.Free;

      // Passing the Keys and the Values in separate arrays; Works without "with" but it would
      // be difficult to maintain for larger number of key/value pairs
      Dict := TDictHelper<Integer, string>.Make([1, 2], ['one', 'two']);
      // Use the array
      for Pair in Dict do
        WriteLn(Pair.Key, ' = ', Pair.Value);
      Dict.Free;

    except on E:Exception do
      WriteLn(E.ClassName, #13#10, E.Message);
    end;
  finally ReadLn;
  end;
end.

      

+2


source


The following example passes an array of keys and values ​​to a custom constructor. Keys and values ​​are placed in the same array using the template: key1, value1, key2, value2, ...., keyN, valueN. The array must contain an even number of elements.

unit MainUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Generics.Collections, System.Rtti;


type
  TForm3 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyDictionary<TK, TV> = class(TDictionary<TK,TV>)
    constructor Create(const values: array of variant);
  end;

var
  Form3: TForm3;
  extensions: TMyDictionary<string, integer>;

implementation

constructor TMyDictionary<TK, TV>.Create(const values: array of variant);
var
  I: Integer;
  k, v: TValue;
  kt: TK;
  vt: TV;
begin
  inherited Create(Length(values) div 2);
  I := Low(values);
  while i <= High(values)  do
  begin
    k := TValue.FromVariant(values[i]);
    v := TValue.FromVariant(values[i + 1]);
    kt := k.AsType<TK>;
    vt := v.AsType<TV>;
    Add(kt, vt);
    Inc(I, 2);
  end;

end;

{$R *.dfm}
begin

 extensions := TMyDictionary<string, integer>.Create(['1', 1, '3', 3]);

 OutputDebugString(PChar(IntToStr(extensions['1'])));
end.

      

I'm not too sure about the performance of the TValue methods, but if you have multiple items, I think it's negligible.

0


source







All Articles