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を使用してリストからアイテムを削除するにはどうすればよいですか?

4

3 に答える 3

11

これを行う再帰的な方法は次のとおりです。

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

すべての操作を 1 つのループにまとめたい場合は、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);
    }
}

もう 1 つの方法は、すべての要素を含むフラット (非階層) リストまたはディクショナリ(最も簡単な方法!) を作成することです。子の親 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>ます。

于 2012-05-09T22:37:01.223 に答える
1

つまり、グラフをトラバースして、アイテムを削除する必要があります。ここにいくつかの質問があります:

  • サイクルはありますか?ノードAには子があり、子Bがあり、BにはCがあり、CはAを指します(A-> B-> C-> Aなど)。
  • 複数のルーツがありますか?
  • マルチグラフですか?

アイテムを削除する際の問題は、子供をどうするかです。ルートがsamGuidを取得した場合はどうなりますか?最善の解決策は、ツリーをトラバースしてノードのコレクションを取得することです。

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

これで、要素をリストからRemove()するか、Where()でフィルタリングできます。

于 2012-05-09T22:42:03.977 に答える
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 をチェックしないことに注意してください (必要に応じてそのチェックを追加できます)。削除するものが何もないため、その場合に何をすべきかわかりませんでした。さらに、ブランチの 1 つが Guid と一致する場合、ブランチのチェックを停止します (そのため、親の Id が一致する場合、一致する ID を持つ可能性があるそのノードの子もチェックしません)。削除されたノードの子をチェックする必要があった場合は、削除する前に子ノードをスタックにプッシュします。

于 2012-05-09T22:26:10.630 に答える