Merging Treenodes

前端 未结 4 1768
借酒劲吻你
借酒劲吻你 2021-01-19 03:41

Does anyone know of an algorithm that will merge treenodes in the following way?

treeA
   \\ child a
          \\node(abc)
   \\ child b
          \\node(xyz         


        
相关标签:
4条回答
  • 2021-01-19 04:28

    Ok, I'll admit, when I first started messing with this, I didn't think it would be too hard, so I figured I'll try to do it using LINQ. It came out to be nuts, but it works. I'm SURE there are more elegant and efficient algorithms, but here it is!

    First, I have a ToEnumerable extension method on the TreeNodeCollection class:

        public static class TreeNodeCollectionExtensions
        {
            public static IEnumerable<TreeNode> ToEnumerable(this TreeNodeCollection nodes)
            {
                foreach (TreeNode node in nodes)
                {
                    yield return node;
                }
            }
        }
    

    Then, I implement a custom comparer:

    public class TreeNodeComparer : IEqualityComparer {

    public bool Equals(TreeNode x, TreeNode y)
    {
        return x.Text == y.Text;
    }
    
    public int GetHashCode(TreeNode obj)
    {
        return obj.Text.GetHashCode();
    }
    

    }

    And finally, the crazyness:

    private TreeView MergeTreeViews(TreeView tv1, TreeView tv2)
    {
        var result = new TreeView();
        foreach (TreeNode node in tv2.Nodes)
        {
            result.Nodes.Add(node.Clone() as TreeNode);
        }
    
        foreach (TreeNode node in tv1.Nodes)
        {
            var nodeOnOtherSide = result.Nodes.ToEnumerable()
                .SingleOrDefault(tr => tr.Text == node.Text);
            if (nodeOnOtherSide == null)
            {
                TreeNode clone = node.Clone() as TreeNode;
                result.Nodes.Add(clone);
    
            }
            else
            {
    
                var n = node.Nodes.ToEnumerable()
                         .Where(t => !(nodeOnOtherSide.Nodes.ToEnumerable()
                         .Contains(t, new TreeNodeComparer())));
    
                foreach (TreeNode subNode in n)
                {
                    TreeNode clone = subNode.Clone() as TreeNode;
                    nodeOnOtherSide.Nodes.Add(clone);
                }
            }
        }
    
        return result;
    }
    

    The way I coded it was that it returns a third "merged" treeView. You can change the code, so that it takes a third treeview as a parameter, so that you can pass in a treeView you may already have.

    Again, I'm SURE there are better way to do this, but it SHOULD work.

    One more thing I'd like to point out, this will only work for a TreeView that is two layers deep.

    0 讨论(0)
  • 2021-01-19 04:28

    If you are using the Node.Name attribute to set the actual path of the item, then a merge is somewhat simple.

    First, create a TreeNodeCollection extension like so (this is needed to have a Case Sensitive Find() method for the TreeNodeCollection, which ensures that the Unique Path can be unique by Case as well) :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace TreeViewApp
    {
        public static class TreeNodeCollectionExtensions
        {
            public static TreeNode[] FindExact(this TreeNodeCollection coll, string keytofind)
            {
                TreeNode[] retval;
    
                if (String.IsNullOrWhiteSpace(keytofind) || coll == null)
                {
                    retval = new TreeNode[0];
                }
                else
                {
                    TreeNode[] badfinds = coll.Find(keytofind, true);
    
                    List<TreeNode> goodfinds = new List<TreeNode>();
                    foreach (TreeNode bad in badfinds)
                    {
                        if (bad.Name == keytofind)
                            goodfinds.Add(bad);
                    }
                    retval = goodfinds.ToArray();
    
                }
                return retval;
            }
        }
    }
    

    Second, fill a treeview with your Source nodes...
    Thrid, fill a treeview with your Target nodes...
    Fourth, create an empty treeview.

    and then, it's as simple as this:

        private void btn_Merge_Click(object sender, EventArgs e)
        {
            //first merge
            foreach (TreeNode sourceNode in this.treeview_Source.Nodes)
            {
                FindOrAdd(sourceNode, ref this.treeview_Merged);
            }
    
            //second merge
            foreach (TreeNode targetNode in this.treeview_Target.Nodes)
            {
                FindOrAdd(targetNode, ref this.treeview_Merged);
            }
        }
    
        private void FindOrAdd(TreeNode FindMe, ref TreeView InHere)
        {
            TreeNode[] found = InHere.Nodes.FindExact(FindMe.Name);
            //if the node is not found, add it at the proper location.
            if (found.Length == 0)
            {
                if (FindMe.Parent != null)
                {
                    TreeNode[] foundParent = InHere.Nodes.FindExact(FindMe.Parent.Name);
                    if (foundParent.Length == 0)
                        InHere.Nodes.Add((TreeNode)FindMe.Clone());
                    else
                        foundParent[0].Nodes.Add((TreeNode)FindMe.Clone());
                }
                else
                    InHere.Nodes.Add((TreeNode)FindMe.Clone());
            }
            else
            {
                //if the item was found, check all children.
                foreach (TreeNode child in FindMe.Nodes)
                    FindOrAdd(child, ref InHere);
            }
        }
    

    Once again, this solution only works if you have unique paths... with the extension, it also accounts for uniqueness at the case level.

    I posted this here in the hopes of helping someone who, like me, had to search for a solution for days on end without success and had to build my own.

    0 讨论(0)
  • 2021-01-19 04:38

    Well, once I actually took the time to think about it, the solution turns out to be far more simple than I anticipated. (I've posted the critical part of the code below)

       private TreeNode DoMerge(TreeNode source, TreeNode target) {
            if (source == null || target == null) return null;
    
            foreach (TreeNode n in source.Nodes) {
                // see if there is a match in target
                var match = FindNode(n, target.Nodes); // match paths
                if (match == null) { // no match was found so add n to the target
                    target.Nodes.Add(n);
                } else { 
                    // a match was found so add the children of match 
                    DoMerge(n, match);
                }
    
            }
            return target;
    
        }
    

    Still interested to know if someone has a better solution?

    0 讨论(0)
  • 2021-01-19 04:39

    I came up with this recursive example, works perfect in C# (have been using it myself), note that you'll need to find a way to convert TreeNode.Nodes to an array:

    public static TreeNode[] mergeTrees(TreeNode[] target, TreeNode[] source)
            {
                if (source == null || source.Length == 0)
                {
                    return target;
                }
                if (target == null || target.Length == 0)
                {
                    return source;
                }
                bool found;
                foreach (TreeNode s in source)
                {
                    found = false;
                    foreach (TreeNode t in target)
                    {
                        if (s.Text.CompareTo(t.Text) == 0)
                        {
                            found = true;
                            TreeNode[] updatedNodes = mergeTrees(Util.treeView2Array(t.Nodes), Util.treeView2Array(s.Nodes));
                            t.Nodes.Clear();
                            t.Nodes.AddRange(updatedNodes);
                            break;
                        }
                    }
                    if (!found)
                    {
                        TreeNode[] newNodes = new TreeNode[target.Length + 1];
                        Array.Copy(target, newNodes, target.Length);
                        newNodes[target.Length] = s;
                        target = newNodes;
                    }
                }
                return target;
            }
    
    0 讨论(0)
提交回复
热议问题