Это эффективный код для многоключевого словаря в теле метода? - PullRequest
1 голос
/ 13 сентября 2011

Я просто хочу спросить, если:

  1. Приведенный ниже код эффективен?
  2. Есть ли лучший способ справиться с этим?
  3. Как кодировать, если необходимы дополнительные значения для пары имя таблицы / имя поля?

Нам нужно использовать многоключевой словарь, который содержит что-то вроде (TableName, FieldName, FieldValue).

Я искал какой-то ответ, но те, которые я нашел до сих пор, не применимы к нашей установке. Мы используем 3.5, поэтому пока нет доступных Tuple. Мы также интегрируем эту логику сценария с приложением, которое позволяет кодировать только «внутри» тела метода, поэтому мы ограничены и не можем создать отдельный класс / структуру и т. Д. Наша установка - C # / VS 2010.

Любая помощь приветствуется. Заранее спасибо!

Dictionary<string, Dictionary<string, string>> tableList = new Dictionary<string, Dictionary<string, string>>();
Dictionary<string, string> fieldList = new Dictionary<string, string>();

// add fields to field list, then add the field lists to the corresponding table list
// clear field list for next table
// values are just hardcoded here to simplify, but is being read from actual objects in the application

fieldList.Add("Field1", "abc");
fieldList.Add("Field2", "def");
fieldList.Add("Field3", "ghi");
fieldList.Add("Field4", "jkl");

tableList.Add("Table1", new Dictionary<string, string>(fieldList));
fieldList.Clear();

fieldList.Add("Field1", "xyz");
fieldList.Add("Field2", "uvw");
fieldList.Add("Field3", "rst");

tableList.Add("Table2", new Dictionary<string, string>(fieldList));
fieldList.Clear();

fieldList.Add("Field1", "123");
fieldList.Add("Field2", "456");

tableList.Add("Table3", new Dictionary<string, string>(fieldList));
fieldList.Clear();

// Display tables and corresponding fields                          

foreach (KeyValuePair<string, Dictionary<string, string>> fieldList4 in tableList)
{
    foreach (KeyValuePair<string, string> fieldList5 in fieldList4.Value)
    {
        txtMessage.Text = txtMessage.Text + "\r\nTable=" + fieldList4.Key + ", Field=" + fieldList5.Key + " - " + fieldList5.Value;
    }
}

// Try to find tables and fields in the lists, and list the value if found

string tableToFind = "Table2";
string fieldToFind = "Field2";
Dictionary<string, string> tableFields = new Dictionary<string, string>();

if (tableList.Keys.Contains(tableToFind) == true)
{
    txtMessage.Text = txtMessage.Text = "\r\nTable=" + tableToFind + " exist in table list";
    tableList.TryGetValue(tableToFind, out tableFields);

    if (tableFields.Keys.Contains(fieldToFind) == true)
    {
        foreach(KeyValuePair<string, string> fieldData  in tableFields)
        {
            if (fieldData.Key == fieldToFind)
            {
                txtMessage.Text = txtMessage.Text + "\r\nTable=" + tableToFind + ", Field=" +  fieldData.Key + 
                                                    " with value=" + fieldData.Value + " exist in table list";
                break;
            }
        }
    }
}

Ответы [ 3 ]

4 голосов
/ 13 сентября 2011

Вы можете использовать компилятор для создания составного ключа для вас: Использование анонимных типов.

var dictionary = new Dictionary<Object, int>();

dictionary.Add(new{Text="A", Number=1}, 1);
dictionary.Add(new{Text="A", Number=2}, 3);
dictionary.Add(new{Text="B", Number=1}, 4);
dictionary.Add(new{Text="B", Number=2}, 5);

var x = dictionary[new{Text="B", Number=2}];

C # будет реализовывать Equals и GetHashcode на основе ваших полей. Таким образом, вы получаете ключ, который будет вести себя так, как вы ожидаете.

2 голосов
/ 13 сентября 2011

В вашем коде целый ряд проблем и неэффективностей.

  1. Если вы собираетесь создать несколько словарей, создайте словари напрямую.Не используйте отдельный экземпляр для заполнения значений и копирования из.

  2. Никогда не используйте конкатенацию строк в таком цикле.Используйте StringBuilder или другой подобный механизм для создания ваших строк.У вас уже есть значения в коллекции, поэтому использование String.Join() в сочетании с LINQ поможет вам в этом.

  3. Ваш подход к получению значений из словаря, по меньшей мере, неудобен.Обычно вы пытаетесь прочитать ключ только один раз TryGetValue().Ваш код использует его неправильно.Если вы собираетесь проверить, существует ли ключ в словаре (используя Contains()), то нет смысла использовать TryGetValue().Что еще хуже, вы сделали это , а затем вручную искали ключ во внутреннем словаре, просматривая пары ключ-значение.

Типичный шаблон выглядит следующим образом:

DictValueType value;
if (myDict.TryGetValue(key, out value))
{
    // key was in the dictionary, the value is stored in the `value` variable
}

Код, который вы могли бы написать гораздо намного более эффективно, как это:

var tableList = new Dictionary<string, Dictionary<string, string>>
{
    { "Table1", new Dictionary<string, string>
                {
                    { "Field1", "abc" },
                    { "Field2", "def" },
                    { "Field3", "ghi" },
                    { "Field4", "jkl" },
                }
    },
    { "Table2", new Dictionary<string, string>
                {
                    { "Field1", "xyz" },
                    { "Field2", "uvw" },
                    { "Field3", "rst" },
                }
    },
    { "Table3", new Dictionary<string, string>
                {
                    { "Field1", "123" },
                    { "Field2", "456" },
                }
    },
};

// Display tables and corresponding fields
txtMessage.Text = String.Join("\r\n",
    tableList.SelectMany(table =>
        table.Value.Select(fieldList =>
            String.Format("Table={0}, Field={1} - {2}",
                table.Key, fieldList.Key, fieldList.Value)
        )
    ).ToArray()
);

// (I hope you have this in a separate method)
// Try to find tables and fields in the lists, and list the value if found
string tableToFind = "Table2";
string fieldToFind = "Field2";

var builder = new StringBuilder(txtMessage.Text); // mostly useful if you have a 
                                                  // lot of different strings to add
Dictionary<string, string> foundTable;
if (tableList.TryGetValue(tableToFind, out foundTable))
{
    builder.AppendLine()
        .Append("Table=" + tableToFind + " exist in table list");

    string foundField;
    if (foundTable.TryGetValue(fieldToFind, out foundField))
    {
        builder.AppendLine()
            .AppendFormat("Table={0}, Field={1} with value={2} exist in table list",
                tableToFind, fieldToFind, foundField);
    }
}
txtMessage.Text = builder.ToString();

Вложенные словари - это не плохо, этохороший способ организовать иерархию ключей и значений.Но для того, чтобы поддерживать его в рабочем состоянии, вы, как правило, должны инкапсулировать все в другом классе, предоставляя методы для манипулирования данными без непосредственного управления словарями.Вы можете сделать это как эффективным, так и ремонтопригодным.Как реализовать это упражнение, оставленное вам.

2 голосов
/ 13 сентября 2011

Я не думаю, что так много словарей были бы «эффективными».

Я думаю, что лучшим способом было бы добавлять значения в один и тот же словарь несколько раз - при условии, что вы хотите иметь возможность индексировать их по одному из признаков (не всем):

dictionary.Add("FField1", "xxx");
dictionary.Add("TTable1", "xxx");

В противном случае используйте присоединительный символ (например, '\0'), если вы хотите проиндексировать их по всем указателям вместе.

dictionary.Add("Table1\0Field1", "xxx");
...