[英]Remove Object From Hierarchical Collection
我在分層列表中有一組 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 從列表中刪除項目,而不管它在列表中的哪個位置?
這是執行此操作的遞歸方式:
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];
flatNodes[nodeToDelete.ParentId].Children.Remove(id);
}
但是,如果您希望 UI 識別更改,則需要使用ObservableCollection<Node>
。
換句話說,您要遍歷一個圖形並刪除和該項目。 這里有一些問題:
刪除項目的問題是您如何處理 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() 對其進行過濾。
我確信 LINQ 忍者可以編寫一些不錯的腳本,但我對此還不夠精明。 至少這里有一些可能對你有用的非遞歸、未經測試的代碼:
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]);
}
}
}
請注意,它不會檢查“根”節點的 ID(如果需要,您可以添加該檢查),只是不知道在這種情況下該怎么做,因為不會有任何要刪除的內容。 此外,如果分支中的一個與 Guid 匹配,它會停止檢查分支(因此,如果父節點的 Id 匹配,它不會檢查可能也具有匹配 Id 的節點的子節點)。 如果您確實需要檢查已刪除節點的子節點,只需在刪除之前將子節點推入堆棧即可。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.