24

I have a function where I store some key- value pairs and when I iterate them I get this error twice: [dcc32 Error] App.pas(137): E2149 Class does not have a default property. Here is part of my code:

function BuildString: string;
var
  i: Integer;
  requestContent: TDictionary<string, string>;
  request: TStringBuilder;
begin
  requestContent := TDictionary<string, string>.Create();

  try
    // add some key-value pairs
    request :=  TStringBuilder.Create;
    try
      for i := 0 to requestContent.Count - 1 do
      begin
        // here I get the errors
        request.Append(requestContent.Keys[i] + '=' +
          TIdURI.URLEncode(requestContent.Values[i]) + '&');
      end;

      Result := request.ToString;
      Result := Result.Substring(0, Result.Length - 1); //remove the last '&'
    finally
      request.Free;
    end; 
  finally
    requestContent.Free;
  end;
end;

I need to collect the information from each item in the dictionary. How can I fix it?

bob_saginowski
  • 1,429
  • 2
  • 20
  • 35
  • 3
    `var S: string; Pair: TPair; begin for Pair in YourDictionary do S := Pair.Key + Pair.Value; end;` – TLama Sep 29 '14 at 12:28
  • 1
    Use `for AKey in requestContent.Keys do begin request.Append(AKey + '=' + TIdURI.Encode(requestContent[AKey]) + '&'); ... etc.`. You must declare `AKey` as string, of course. – Rudy Velthuis Sep 29 '14 at 12:29
  • 2
    @RudyVelthuis With dictionaries, it is almost always better to iterate over pairs than over keys. Doing so invariably results in more efficient code. In the case of the Delphi dictionary, the pair iterator avoids any need to calculate hash codes, and perform probing. – David Heffernan Sep 29 '14 at 12:34
  • @David: I saw your answer and +1-ed it. I agree. I first thought of a solution that was closest to the question, but getting the pairs is indeed better. – Rudy Velthuis Sep 29 '14 at 12:35

1 Answers1

51

The Keys and Values properties of your dictionary class are of type TDictionary<string, string>.TKeyCollection and TDictionary<string, string>.TValueCollection respectively. These classes are derived from TEnumerable<T> and cannot be iterated by index. You can however iterate over Keys, or indeed Values, not that doing the latter would be of much use to you.

If you iterated over Keys your code might look like this:

var
  Key: string;
....
for Key in requestContent.Keys do
  request.Append(Key + '=' + TIdURI.URLEncode(requestContent[Key]) + '&');

This however is inefficient. Since you know that you want both key and matching value, you can use the dictionary's iterator:

var 
  Item: TPair<string, string>; 
....
for Item in requestContent do 
  request.Append(Item.Key + '=' + TIdURI.URLEncode(Item.Value) + '&');

The pair iterator is more efficient than the first variant above. This is because the implementation details mean that the pair iterator is able to iterate the dictionary without:

  1. Calculating hash codes for each key, and
  2. Performing linear probing when hash codes collide.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490