delphiloopstdictionary

delphi dictionary iterating


g'morning!

i fill a dictionary TDictionary<String, TStringlist> (delphi-collections-unit) with strings as values and several strings as values. something like:

(without "," of course). what i need is to iterate this dictionary and to multiply out the values with the keys. output should be like

so every combination. how can i do so? the number of keys is dynamic and so is the size of the tstringlist. thanks! SOLVED by now...

now the problem with the scope. following is the procedure that fills the dict. the subsplits and the splitstring are stringlists, that get freed at the end of the procedure. the dict is created after the procedures-block (in main? how is it called?), the fill-method is called and then i want to do recursion like in the code-example but there are no values in the dict....

while not Eof(testfile) do
  begin
    ReadLn(testfile, text);
    if AnsiContainsStr(text, '=') then
    begin
      Split('=', text, splitarray);
      splitarray[0] := trim(splitarray[0]);
      splitarray[1] := DeleteSpaces(splitarray[1]);
      if AnsiStartsStr('data', splitarray[0]) then
      begin
        split(' ', splitarray[0], subsplit1);
        splitarray[0]:=subsplit1[1];
        split(',', splitarray[1], subsplit2);
        dict.Add(splitarray[0], subsplit2);
        for ValueName in dict.Values do
        begin
          for i := 0 to Valuename.Count - 1 do
          write('Values are : '+ Valuename[i]);
        writeln;
        end;//
      end;//
    end;//
  end;//

Solution

  • What you want is made a bit complicated by the use of the TDictionary<string, TStringList>, because that implies variable number of keys. If it weren't for the variable number of keys, you wouldn't need a dictionary and you'd simply iterate over 3 TStringLists.

    That said, you've got the classic "generate all permutations" problem. It can be solved using recursion or backtracking. Recursion is simpler to implement, backtracking uses less stack space. The choice is yours. Here's a complete console application that does the whole deal, from initializing the dictionary, populating the dictionary, generating all permutations using a recursive function.

    program Project23;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils, Classes, Generics.Collections;
    
    var
      Dict:TDictionary<string, TStringList>;
      L: TStringList;
      KeyName: string;
      KeysList: TStringList;
    
    // Help procedure, adds a bunch of values to a "Key" in the dictionary
    procedure QuickAddToDict(KeyName:string; values: array of string);
    var L: TStringList;
        s: string;
    begin
      // Try to get the TStringList from the dictionary. If we can't get it
      // we'll create a new one and add it to the dictionary
      if not Dict.TryGetValue(KeyName, L) then
      begin
        L := TStringList.Create;
        Dict.Add(KeyName, L);
      end;
      // Iterate over the values array and add stuff to the TStringList
      for s in values do
        L.Add(s);
    end;
    
    // Recursive routine to handle one KEY in the dictionary
    procedure HandleOneKey(KeyIndex:Integer; PrevKeys:string);
    var L:TStringList;
        i:Integer;
        Part: string;
        KeyName: string;
    begin
      KeyName := KeysList[KeyIndex];
      L := Dict[KeyName];
      for i:=0 to L.Count-1 do
      begin
        Part := KeyName + '=' + L[i];
        if KeyIndex = (KeysList.Count-1) then
          WriteLn(PrevKeys + ' ' + Part) // This is a solution, we're at the last key
        else
          HandleOneKey(KeyIndex+1, PrevKeys + ' ' + Part); // Not at the last key, recursive call for the next key
      end;
    end;
    
    begin
      try
        Dict := TDictionary<string, TStringList>.Create;
        try
    
          // Add whatever you want to the Dict.
          // Using the helper routine to set up the dictionary faster.
          QuickAddToDict('names', ['john', 'lisa', 'stan']);
          QuickAddToDict('skills', ['read', 'write', 'speak']);
          QuickAddToDict('ages', ['12', '14', '16']);
    
          // Extract all the keys to a string list. Unfortunately the dictionary
          // doesn't offer a way to get a key name by index, so we have to use the
          // keys iterator to extract all keys first.
          KeysList := TStringList.Create;
          try
            for KeyName in Dict.Keys do
              KeysList.Add(KeyName);
            if KeysList.Count > 0 then
            begin
              // We got at least one key, we can start the recursive process.
              HandleOneKey(0, '');
            end;
          finally KeysList.Free;
          end;
    
          WriteLn;
          WriteLn('Press ENTER to make the window go away');
          ReadLn;
    
        finally
          // TDictionary doesn't own the keys or the values. Strings are managed types in
          // delphi, we don't need to worry about them, but we do need to free the TStringList's
          // We use the Values iterator for that!
          for L in Dict.Values do
            L.Free;
          Dict.Free;
        end;
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    end.