Можно ли оптимизировать этот код?

Мне нужно отобразить TreeView в WinForm, основанном на сложном словаре, и я ищу "самый короткий" способ сделать это. Я думаю, что это может быть сделано в одном запросе LINQ, но я не знаю как, и я даже не уверен, возможно ли это.

Вот пример словаря записей:

Dictionary<String, String> dict = new Dictionary<String, String>()
{
  {"aaa.bbb.ccc.ddd", "value1"},
  {"aaa.bbb.ccc.eee", "value2"},
  {"aaa.bbb.fff.ggg", "value3"},
  {"aaa.hhh.iii.jjj", "value4"},
  {"bbb.ddd", "value5"},
  {"ccc", "value6"}
};

И я хочу вернуть TreeView так:

|--+ aaa
|  |--+ bbb
|  |  |--+ ccc
|  |  |  |--- ddd = value1
|  |  |  |--- eee = value2
|  |  |
|  |  |--+ fff
|  |  |  |--- ggg = value3
|  |
|  |--+ hhh
|  |  |--+ iii
|  |  |  |--- jjj = value4
|
|--+ bbb
|  |--- ddd = value5
|
|--+ ccc = value6

И вот, что я получил сейчас (я еще не обработал значение):

List<String> list = new List<string>() { 
    "aaa.bbb.ccc.ddd", 
    "aaa.bbb.ccc.eee",
    "aaa.bbb.fff.ddd",
    "aaa.bbb.fff.ggg",
    "aaa.ggg.fff.hhh",
    "hhh.iii.jjj.kkk"
};

Action<TreeNode, String> traverse = null;

traverse = (node, chaine) =>
{
    String[] tab = chaine.Split(new char[] { '.' }, 2);

    TreeNode child = null;
    if (node.Nodes.ContainsKey(tab[0]))
    {
        child = node.Nodes[tab[0]];
    }
    else
    {
        child = node.Nodes.Add(tab[0]); // + ((tab.Length > 1) ? " - " + tab[1] : ""));
        child.Name = tab[0];
    }

    if (tab.Length > 1 && !String.IsNullOrEmpty(tab[1]))
        traverse(child, tab[1]);
};

TreeNode test = this.treeView1.Nodes.Add("test");

list.ForEach(x => traverse(test, x));

Я надеюсь, что я достаточно ясно в своем объяснении.

1 ответ

Решение

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

var query = from this in that where this is t select this

Но то, что вы могли бы сделать, это переписать немного, что-то вроде:

    public void AnotherWay()
    {
        TreeNode parent = this.treeView1.Nodes.Add("test");

        List<String> list = new List<String>() 
        { 
            "aaa.bbb.ccc.ddd", 
            "aaa.bbb.ccc.eee",
            "aaa.bbb.fff.ddd",
            "aaa.bbb.fff.ggg",
            "aaa.ggg.fff.hhh",
            "hhh.iii.jjj.kkk"
        };

        list.ForEach(x =>
            {
                TreeNode root = parent;
                TreeNode child = null;

                x.Split(new[] { '.' })
                    .ToList()
                    .ForEach(i =>
                    {
                        child = root.Nodes.ContainsKey(i) ?
                            root.Nodes[i] :
                            root.Nodes.Add(i);

                        child.Name = i;
                        root = child;
                    });
            });
    }

Этот код делает именно то, что вы уже опубликовали, но он меньше и IMO читается немного яснее.

Как правило, я вижу Action или Func, который выполняет достаточную логику, чтобы быть запахом кода, особенно если он используется несколько раз (в этом случае он должен быть извлечен в свой собственный метод, но я отвлекся).

В вашем случае, хотя кажется, что действие используется только один раз в строке list.ForEach(x => traverse(test, x)); и поэтому функциональность может просто заменить ваш призыв к действию, как в моем примере выше. (Однако, если сложность логики или LOC увеличилась, то в этом примере я бы испытал соблазн перенести функциональность в свой собственный метод для удобства обслуживания).

Этот подход также позволяет вам легко удовлетворить ваше второе требование обработки словарей с небольшими изменениями, такими как:

    public void DictFunc()
    {
        TreeNode parent = this.treeView1.Nodes.Add("test");

        Dictionary<String, String> dict = new Dictionary<String, String>()
        {
            { "aaa.bbb.ccc.ddd", "Value1" },
            { "aaa.bbb.ccc.eee", "Value2" },
            { "aaa.bbb.fff.ddd", "Value3" },
            { "aaa.bbb.fff.ggg", "Value4" },
            { "aaa.ggg.fff.hhh", "Value5" },
            { "hhh.iii.jjj.kkk", "Value6" }
        };

        dict.ToList().ForEach(x =>
        {
            // For brevity, same as logic in example above.
            // Plus the small amount (~three LOC) of additional logic
            // required to handle the values.
        });
    }

Я оставил обработку значения словаря в качестве упражнения для вас, но остальная часть логики внутри ForEach идентично тому, что в моем первом примере.

Другие вопросы по тегам