Можно ли оптимизировать этот код?
Мне нужно отобразить 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
идентично тому, что в моем первом примере.