2012-05-09 136 views
3

我有一個分層列表中的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從列表中刪除項目,而不管它在列表中的位置?

+1

可以刪除節點的孩子嗎? – Joe

+0

如果您希望UI反映更改,則需要使用observablecollections而不是列表。 – mdm20

+0

LINQ只是查詢,你不能用LINQ「刪除」。 –

回答

7

這裏這樣做的遞歸的方式:

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); 
    } 
} 

如果您想擁有所有的操作在一個循環中,有一個循環做到這一點。但在我看來,閱讀起來要困難得多。

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。在某些情況下,特別是當您擁有大量物品的深層樹木時,這種方式會更加高效。如果你想在UI認識到,你需要使用ObservableCollection<Node>變化

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); 
} 

,雖然:如果你想刪除某一個項目,像這樣做。

+0

工作正常。謝謝 – CoderForHire

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]); 
     } 
    } 
} 

需要注意的是它不檢查「根」節點的ID(你可以,如果你添加檢查想),只是不知道該怎麼做,因爲沒有任何東西可以刪除。此外,如果其中一個與Guid匹配,則停止檢查分支(因此,如果父母的Id匹配,則不會檢查該節點的子節點是否具有匹配的ID)。如果您確實需要檢查已刪除節點的子節點,只需將子節點推入堆棧,然後再將其移除。

+0

當您匹配guid時,清理堆棧並跳出for循環可能會更好。 – mdm20

+0

這假定只有一個匹配的條目。如果有多個(從描述中看來是這樣的話),那麼你需要繼續搜索其他分支。 –

0

換句話說,你想遍歷一個圖並刪除該項。這裏有一些問題:

  • 它可以有周期嗎?節點A有一個孩子,它有一個孩子B,B的C和C指向A(A→B→C→A等等)
  • 是否有多個根?
  • 它是多圖嗎?

刪除一個項目的問題是你怎麼用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(); 

現在你可以刪除()的元素形成的列表或過濾它在哪裏()。