我在分层列表中有一组NodeObject类。该列表可以是任意数量的级别。
public class NodeModel : ViewModelBase
{
public Guid Id { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public NodeType Type { get; set; }
public List<NodeModel> Children { get; set; }
}
如何使用其Guid ID从列表中删除项目,无论其在列表中的位置如何?
答案 0 :(得分:9)
这是一种递归方式:
private void DeleteNode(IList<Node> nodes, Guid id)
{
Node nodeToDelete = null;
foreach (var node in nodes)
{
if (node.Id == id)
{
nodeToDelete = node;
break;
}
DeleteNode(node.Children, id);
}
if (nodeToDelete != null)
{
nodes.Remove(nodeToDelete);
}
}
如果您想在一个循环中完成所有操作,请使用for循环执行此操作。但在我看来,阅读起来要困难得多。
private void DeleteNode(IList<Node> nodes, int id)
{
for (var index = 0; index < nodes.Count; index++)
{
var currentNode = nodes[index];
if (currentNode.Id == id)
{
nodes.Remove(currentNode);
break;
}
DeleteNode(currentNode.Children, id);
}
}
另一种方法是拥有一个扁平(非分层)列表或偶数字典(最快的方式!),其中包含所有元素。您可以添加另一个属性,其中包含子项的父ID。在某些情况下,特别是当您拥有包含大量物品的深树时,这种方式会更加高效。如果要删除某个项目,请执行以下操作:
private void DeleteNode(IList<Node> flatNodes, Guid id)
{
var nodeToDelete = flatNodes.FirstOrDefault(n => n.Id == id);
if (nodeToDelete != null)
{
var parent = flatNodes.First(n => n.Id == nodeToDelete.ParentId);
parent.Children.Remove(nodeToDelete);
}
}
private void DeleteNodeFromFlatDictionary(IDictionary<Guid, Node> flatNodes, Guid id)
{
if (!flatNodes.ContainsKey(id)) return;
var nodeToDelete = flatNodes[id];
parent[nodeToDelete.ParentId].Children.Remove(id);
}
如果您希望用户界面识别您需要使用ObservableCollection<Node>
的更改。
答案 1 :(得分:1)
换句话说,您想要遍历图表并删除项目。这是一些问题:
删除项目的问题是你如何处理childern?如果root获得sam Guid怎么办?最好的解决方案是遍历树并获取节点集合。
public static IEnumerable<T> Traverse<T>(T root, Func<T, IEnumerable<T>> children)
{
var seen = new HashSet<T>();
var stack = new Stack<T>();
stack.Push(root);
while(stack.Count != 0)
{
T item = stack.Pop();
if (seen.Contains(item))
continue;
seen.Add(item);
yield return item;
foreach(var child in children(item))
stack.Push(child);
}
}
然后致电
var nodes = Traverse<NodeModel>(root, node => node.Children).ToList();
现在你可以从列表中删除()元素或用Where()。
过滤它答案 2 :(得分:0)
我确信一个LINQ-ninja可以掀起一些不错的剧本,但我还不够精明。至少这里有一些非递归的, 未经测试的 代码,可能对您有用:
public void RemoveNodeModelByGuid(NodeModel root, Guid guid)
{
Stack<NodeModel> nodes = new Stack<NodeModel>();
nodes.Add(root);
while (nodes.Count > 0)
{
var currentNode = nodes.Pop();
for (int i = currentNode.Children.Count - 1; i >= 0; i--)
{
if (currentNode.Children[i].Id == guid)
currentNode.Children.RemoveAt(i);
else
nodes.Push(currentNode.Children[i]);
}
}
}
请注意,它不会检查“root”节点的Id(如果需要,可以添加该检查),在这种情况下只是不知道该怎么做,因为没有任何要删除的内容。此外,如果其中一个与Guid匹配,它会停止检查分支(因此,如果父级的ID匹配,则它不会检查该节点的子节点,也可能具有匹配的ID)。如果确实需要检查已删除节点的子节点,只需将子节点推入堆栈,然后再将其删除。