using System.Collections;
using System.Collections.Generic;
using System.Globalization;
public static void Main()
scene = new Node<string>("Root");
var textToSave = JsonConvert.SerializeObject(scene);
Console.WriteLine(textToSave);
public class Node<T> : IEqualityComparer, IEnumerable<T>, IEnumerable<Node<T>>
public Node<T> Parent { get; private set; }
public T Value { get; set; }
private readonly List<Node<T>> _children = new List<Node<T>>();
public Node<T> this[int index]
public Node<T> Add(T value, int index = -1)
var childNode = new Node<T>(value);
public void Add(Node<T> childNode, int index = -1)
throw new ArgumentException("The index can not be lower then -1");
if (index > Children.Count() - 1)
throw new ArgumentException("The index ({0}) can not be higher then index of the last iten. Use the AddChild() method without an index to add at the end".FormatInvariant(index));
throw new ArgumentException("The child node with value [{0}] can not be added because it is not a root node.".FormatInvariant(childNode.Value));
throw new ArgumentException("The child node with value [{0}] is the rootnode of the parent.".FormatInvariant(childNode.Value));
if (childNode.SelfAndDescendants.Any(n => this == n))
throw new ArgumentException("The childnode with value [{0}] can not be added to itself or its descendants.".FormatInvariant(childNode.Value));
_children.Add(childNode);
_children.Insert(index, childNode);
public Node<T> AddFirstChild(T value)
var childNode = new Node<T>(value);
AddFirstChild(childNode);
public void AddFirstChild(Node<T> childNode)
public Node<T> AddFirstSibling(T value)
var childNode = new Node<T>(value);
AddFirstSibling(childNode);
public void AddFirstSibling(Node<T> childNode)
Parent.AddFirstChild(childNode);
public Node<T> AddLastSibling(T value)
var childNode = new Node<T>(value);
AddLastSibling(childNode);
public void AddLastSibling(Node<T> childNode)
public Node<T> AddParent(T value)
var newNode = new Node<T>(value);
public void AddParent(Node<T> parentNode)
throw new ArgumentException("This node [{0}] already has a parent".FormatInvariant(Value), "parentNode");
public IEnumerable<Node<T>> Ancestors
return Enumerable.Empty<Node<T>>();
return Parent.ToIEnumarable().Concat(Parent.Ancestors);
public IEnumerable<Node<T>> Descendants
return SelfAndDescendants.Skip(1);
public IEnumerable<Node<T>> Children
public IEnumerable<Node<T>> Siblings
return SelfAndSiblings.Where(Other);
private bool Other(Node<T> node)
return !ReferenceEquals(node, this);
public IEnumerable<Node<T>> SelfAndChildren
return this.ToIEnumarable().Concat(Children);
public IEnumerable<Node<T>> SelfAndAncestors
return this.ToIEnumarable().Concat(Ancestors);
public IEnumerable<Node<T>> SelfAndDescendants
return this.ToIEnumarable().Concat(Children.SelectMany(c => c.SelfAndDescendants));
public IEnumerable<Node<T>> SelfAndSiblings
return this.ToIEnumarable();
public IEnumerable<Node<T>> All
return Root.SelfAndDescendants;
public IEnumerable<Node<T>> SameLevel
return SelfAndSameLevel.Where(Other);
return Ancestors.Count();
public IEnumerable<Node<T>> SelfAndSameLevel
return GetNodesAtLevel(Level);
public IEnumerable<Node<T>> GetNodesAtLevel(int level)
return Root.GetNodesAtLevelInternal(level);
private IEnumerable<Node<T>> GetNodesAtLevelInternal(int level)
return this.ToIEnumarable();
return Children.SelectMany(c => c.GetNodesAtLevelInternal(level));
return SelfAndAncestors.Last();
throw new InvalidOperationException("The root node [{0}] can not get disconnected from a parent.".FormatInvariant(Value));
Parent._children.Remove(this);
get { return Parent == null; }
IEnumerator<T> IEnumerable<T>.GetEnumerator()
return _children.Values().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return _children.GetEnumerator();
public IEnumerator<Node<T>> GetEnumerator()
return _children.GetEnumerator();
public override string ToString()
public static IEnumerable<Node<T>> CreateTree<TId>(IEnumerable<T> values, Func<T, TId> idSelector, Func<T, TId?> parentIdSelector)
var valuesCache = values.ToList();
return Enumerable.Empty<Node<T>>();
T itemWithIdAndParentIdIsTheSame = valuesCache.FirstOrDefault(v => IsSameId(idSelector(v), parentIdSelector(v)));
if (itemWithIdAndParentIdIsTheSame != null)
throw new ArgumentException("At least one value has the samen Id and parentId [{0}]".FormatInvariant(itemWithIdAndParentIdIsTheSame));
var nodes = valuesCache.Select(v => new Node<T>(v));
return CreateTree(nodes, idSelector, parentIdSelector);
public static IEnumerable<Node<T>> CreateTree<TId>(IEnumerable<Node<T>> rootNodes, Func<T, TId> idSelector, Func<T, TId?> parentIdSelector)
var rootNodesCache = rootNodes.ToList();
var duplicates = rootNodesCache.Duplicates(n => n).ToList();
throw new ArgumentException("One or more values contains {0} duplicate keys. The first duplicate is: [{1}]".FormatInvariant(duplicates.Count, duplicates[0]));
foreach (var rootNode in rootNodesCache)
var parentId = parentIdSelector(rootNode.Value);
var parent = rootNodesCache.FirstOrDefault(n => IsSameId(idSelector(n.Value), parentId));
else if (parentId != null)
throw new ArgumentException("A value has the parent ID [{0}] but no other nodes has this ID".FormatInvariant(parentId.Value));
var result = rootNodesCache.Where(n => n.IsRoot);
private static bool IsSameId<TId>(TId id, TId? parentId)
return parentId != null && id.Equals(parentId.Value);
public static bool operator ==(Node<T> value1, Node<T> value2)
if ((object)(value1) == null && (object)value2 == null)
return ReferenceEquals(value1, value2);
public static bool operator !=(Node<T> value1, Node<T> value2)
return !(value1 == value2);
public override bool Equals(Object anderePeriode)
var valueThisType = anderePeriode as Node<T>;
return this == valueThisType;
public bool Equals(Node<T> value)
public bool Equals(Node<T> value1, Node<T> value2)
bool IEqualityComparer.Equals(object value1, object value2)
var valueThisType1 = value1 as Node<T>;
var valueThisType2 = value2 as Node<T>;
return Equals(valueThisType1, valueThisType2);
public int GetHashCode(object obj)
return GetHashCode(obj as Node<T>);
public override int GetHashCode()
return GetHashCode(this);
public int GetHashCode(Node<T> value)
return base.GetHashCode();
public static class NodeExtensions
public static IEnumerable<T> Values<T>(this IEnumerable<Node<T>> nodes)
return nodes.Select(n => n.Value);
public static class OtherExtensions
public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
var grouped = source.GroupBy(selector);
var moreThen1 = grouped.Where(i => i.IsMultiple());
return moreThen1.SelectMany(i => i);
public static bool IsMultiple<T>(this IEnumerable<T> source)
var enumerator = source.GetEnumerator();
return enumerator.MoveNext() && enumerator.MoveNext();
public static IEnumerable<T> ToIEnumarable<T>(this T item)
public static string FormatInvariant(this string text, params object[] parameters)
return string.Format(CultureInfo.InvariantCulture, text, parameters);