AssembleTreeSample_ja - Houzkin/TreeStructures GitHub Wiki
まずは使用するノードを定義します。
public class NamedNode : GeneralTreeNode<NamedNode> {
public NamedNode() { }
public string Name { get; set; }
}public static partial class SampleA {
public static void Method() {
var A = new NamedNode { Name = "A" };
var B = new NamedNode { Name = "B"};
var C = new NamedNode { Name = "C"};
var D = new NamedNode { Name = "D"};
var E = new NamedNode { Name = "E"};
var F = new NamedNode { Name = "F"};
var G = new NamedNode { Name = "G" };
var H = new NamedNode { Name = "H"};
var I = new NamedNode { Name = "I"};
var J = new NamedNode { Name = "J" };
var K = new NamedNode { Name = "K" };
var L = new NamedNode { Name = "L"};
var M = new NamedNode { Name = "M"};
var N = new NamedNode { Name = "N"};
var O = new NamedNode { Name = "O"};
var P = new NamedNode { Name = "P"};
Console.WriteLine("Assemble using the AddChild method.");
A.AddChild(B).AddChild(C).AddChild(D);
B.AddChild(E).AddChild(F);
F.AddChild(G).AddChild(H);
C.AddChild(I);
I.AddChild(J).AddChild(K).AddChild(L);
D.AddChild(M);
M.AddChild(N);
N.AddChild(O).AddChild(P);
Console.WriteLine(A.ToTreeDiagram(x => x.Name));
Console.WriteLine("Displaying height, level, and node index as additional information.");
Console.WriteLine(A.ToTreeDiagram(x => $"Name:{x.Name},Height:{x.Height()},Depth:{x.Depth()},TreeIndex:{x.TreeIndex()}"));
ノードを移動させるには、AddChildによって指定したノードの子ノードとして追加できます。
ただし、ノードが循環する、または兄弟ノードと重複する場合は追加できません。
Console.WriteLine("Move nodeN to be a child node of nodeE");
E.AddChild(N);
Console.WriteLine(A.ToTreeDiagram(x => x.Name));
Console.WriteLine("Move nodeB to be a child node of nodeF (Failure: Cannot add due to cyclic relationship)");
F.AddChild(B);
Console.WriteLine(A.ToTreeDiagram(x => x.Name));
Console.WriteLine("Add nodeD to nodeA (Failure: Cannot add duplicate child nodes)");
A.AddChild(D);
Console.WriteLine(A.ToTreeDiagram(x => x.Name));
A.Disassemble();

Console.WriteLine("Assembling from a Dictionary with specified indices.");
var dic = new Dictionary<int[], NamedNode>() {
[new int[] { }] = A,
[new int[] { 0 }] = I,
[new int[] { 0, 0 }] = C,
[new int[] { 0, 1 }] = L,
[new int[] { 0, 0, 0, }] = D,
[new int[] { 0, 0, 0, 0 }] = E,
[new int[] { 0, 0, 0, 1 }] = J,
[new int[] { 0, 0, 0, 0, 0 }] = F,
[new int[] { 0, 0, 0, 0, 1 }] = K,
[new int[] { 1 }] = G,
[new int[] { 1, 0 }] = H,
[new int[] { 1, 1 }] = B,
[new int[] { 1, 2 }] = M,
[new int[] { 1, 3 }] = N,
[new int[] { 1, 0, 0 }] = O,
[new int[] { 1, 3, 0 }] = P,
};
Console.WriteLine(dic.AssembleTree().ToTreeDiagram(x => x.Name));
Console.WriteLine("Displaying additional information such as indices assigned from parent nodes and paths.");
Console.WriteLine(A.ToTreeDiagram(x => $"Name:{x.Name},BranchIndex:{x.BranchIndex()},NodePath:{x.NodePath(y=>y.Name)}"));
A.Disassemble();
Console.ReadLine();
Console.WriteLine("Building a collection as an N-ary tree.");
var root = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray().Select(x => x.ToString())
.AssembleAsNAryTree(3, x => new NamedNode() { Name = x });
Console.WriteLine(root.ToTreeDiagram(x => x.Name));
Console.WriteLine($"Levelorder:{string.Join(",", root.Levelorder().Select(x=>x.Name))}");
Console.WriteLine($"Preorder:{string.Join(",", root.Preorder().Select(x => x.Name))}");
Console.WriteLine($"Postorder:{string.Join(",", root.Postorder().Select(x => x.Name))}");
Console.WriteLine($"Inorder:{string.Join(",", root.Inorder().Select(x => x.Name))}");
Console.WriteLine("\n ----- Pickup C node -----");
var nodeC = root.Levelorder().First(x => x.Name == "C");
Console.WriteLine($"Upstream:{string.Join(",", nodeC.Upstream().Select(x=>x.Name))}");
Console.WriteLine($"Ancestors:{string.Join(",", nodeC.Ancestors().Select(x => x.Name))}");
Console.WriteLine($"Leafs:{string.Join(", ", nodeC.Leafs().Select(x=>x.Name))}");
Console.WriteLine($"Genarations:{string.Join(",", nodeC.Generations().Select(x => x.Name))}");
Console.WriteLine($"Siblings:{string.Join(",", nodeC.Siblings().Select(x=>x.Name))}");
Console.WriteLine($"Width:{nodeC.Width()}");
Console.WriteLine("\n ----- Pickup R node -----");
var nodeR = root.Levelorder().First(x => x.Name == "R");
Console.WriteLine($"Upstream:{string.Join(",", nodeR.Upstream().Select(x => x.Name))}");
Console.WriteLine($"Ancestors:{string.Join(",", nodeR.Ancestors().Select(x => x.Name))}");
Console.WriteLine($"Leafs:{string.Join(", ", nodeR.Leafs().Select(x => x.Name))}");
Console.WriteLine($"Genarations:{string.Join(",", nodeR.Generations().Select(x => x.Name))}");
Console.WriteLine($"Siblings:{string.Join(",", nodeR.Siblings().Select(x => x.Name))}");
Console.WriteLine($"Width:{nodeR.Width()}");
Console.WriteLine($"Root:{nodeR.Root().Name}");
Console.ReadLine();
}
}

var pathlist = new List<NodePath<string>>() {
new("A","BB"),
new("A"),
new("A","C"),
new("A","C","D"),
new("A","B"),
new("A","C","E"),
new("A","F"),
new("A","G","O","R"),
new("A","B","D"),
new("A","B","D","O"),
};
var pt = pathlist.AssembleTreeByPath(x => new NamedNode() { Name = x.Last() });
Console.WriteLine(pt.ToTreeDiagram(x => x.Name));
/*
A
├ BB
├ C
│ ├ D
│ └ E
├ B
│ └ D
│ └ O
├ F
└ G
└ O
└ R
*/
pt.RemoveAllDescendant(a => a.Name == "D");
Console.WriteLine(pt.ToTreeDiagram(x => x.Name));
/*
A
├ BB
├ C
│ └ E
├ B
├ F
└ G
└ O
└ R
*/ツリー構築に必要なすべてのノードに対するパスが含まれている場合、Dictionaryから構築することもできます。
上記、pathlistにはノードパス("A","G")が存在しないので、Dictionaryから構築した場合、それより末端のノードは追加されません。
var pathDic = pathlist.ToDictionary(x => x, x => new NamedNode() { Name = x.Last() });
var ptt = pathDic.AssembleTreeByPath();
Console.WriteLine(ptt.ToTreeDiagram(x => x.Name));
/*
A
├ BB
├ C
│ ├ D
│ └ E
├ B
│ └ D
│ └ O
└ F
*/複数のルートが存在する場合は AssembleForestByPath を使用します。
pathlist.AddRange( new List<NodePath<string>>() { new("FF"),new("FF", "G"),});
foreach(var p in pathlist.AssembleForestByPath(x=> new NamedNode(){ Name = x.Last() })){
Console.WriteLine(p.ToTreeDiagram(x => x.Name));
}
/*
A
├ BB
├ C
│ ├ D
│ └ E
├ B
│ └ D
│ └ O
├ F
└ G
└ O
└ R
FF
└ G
*/ノードの追加メソッドの戻り値はSelf、削除メソッドの戻り値は削除された子ノードが返ってきます。
拡張メソッドのTry(Add|Remove)Childメソッドの場合、
正常に追加された => true, self
既に追加済みまたは、追加できなかった => false, child
正常に削除された => true, child
コレクションに存在しないまたは、削除できなかった => false, self
TryRemoveOwnメソッドでは何れもselfが返ってきます。