using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Developpez.Dotnet.Collections
{
public static partial class EnumerableExtensions
{
#region ToHierarchy
///
/// Représente un noeud dans une hiérarchie d'objets
///
/// Type des objets de la hiérarchie
public class Node
{
internal Node() { }
///
/// Objet contenu par ce noeud
///
public T Item { get; internal set; }
///
/// Niveau du noeud dans la hiérarchie (0 pour un noeud racine)
///
public int Level { get; internal set; }
///
/// Noeud parent dans la hiérarchie (null pour un noeud racine)
///
public Node Parent { get; internal set; }
///
/// Noeuds enfants dans la hiérarchie
///
public IList> Children { get; internal set; }
}
///
/// Renvoie une collection de noeuds représentant les objets de la collection source sous forme hiérarchique,
/// selon la relation spécifiée
///
/// Type des éléments de la collection source
/// Collection d'objets à hiérarchiser
/// Prédicat pour identifier les éléments racines
/// Relation qui lie un objet parent à ses enfants
/// Une collection de noeuds représentant les objets de la collection source sous forme hiérarchique
public static IEnumerable> ToHierarchy(
this IEnumerable source,
Func startWith,
Func connectBy)
{
return source.ToHierarchy(startWith, connectBy, null);
}
private static IEnumerable> ToHierarchy(
this IEnumerable source,
Func startWith,
Func connectBy,
Node parent)
{
int level = (parent == null ? 0 : parent.Level + 1);
if (source == null)
throw new ArgumentNullException("source");
if (startWith == null)
throw new ArgumentNullException("startWith");
if (connectBy == null)
throw new ArgumentNullException("connectBy");
foreach (T value in from item in source
where startWith(item)
select item)
{
var children = new List>();
var newNode = new Node
{
Level = level,
Parent = parent,
Item = value,
Children = children.AsReadOnly()
};
T tmpValue = value;
children.AddRange(source.ToHierarchy(possibleSub => connectBy(tmpValue, possibleSub), connectBy, newNode));
yield return newNode;
}
}
#endregion
#region DumpHierarchy
///
/// Ecrit une hiérarchie d'objets dans le TextWriter spécifié
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
/// TextWriter dans lequel écrire la hiérarchie
/// Chaine d'indentation à utiliser
/// Fonction qui sélectionne le membre à afficher
public static void DumpHierarchy(this IEnumerable> nodes, TextWriter writer, string indent, Func display)
{
nodes.DumpHierarchy(writer, indent, display, 0);
}
///
/// Ecrit une hiérarchie d'objets dans le TextWriter spécifié
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
/// TextWriter dans lequel écrire la hiérarchie
/// Fonction qui sélectionne le membre à afficher
public static void DumpHierarchy(this IEnumerable> nodes, TextWriter writer, Func display)
{
nodes.DumpHierarchy(writer, " ", display, 0);
}
///
/// Ecrit une hiérarchie d'objets dans le TextWriter spécifié
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
/// TextWriter dans lequel écrire la hiérarchie
/// Chaine d'indentation à utiliser
public static void DumpHierarchy(this IEnumerable> nodes, TextWriter writer, string indent)
{
nodes.DumpHierarchy(writer, indent, item => item.ToString(), 0);
}
///
/// Ecrit une hiérarchie d'objets dans le TextWriter spécifié
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
/// TextWriter dans lequel écrire la hiérarchie
public static void DumpHierarchy(this IEnumerable> nodes, TextWriter writer)
{
nodes.DumpHierarchy(writer, " ", item => item.ToString(), 0);
}
///
/// Ecrit une hiérarchie d'objets dans la console
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
/// Chaine d'indentation à utiliser
/// Fonction qui sélectionne le membre à afficher
public static void DumpHierarchy(this IEnumerable> nodes, string indent, Func display)
{
nodes.DumpHierarchy(Console.Out, indent, display, 0);
}
///
/// Ecrit une hiérarchie d'objets dans la console
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
/// Fonction qui sélectionne le membre à afficher
public static void DumpHierarchy(this IEnumerable> nodes, Func display)
{
nodes.DumpHierarchy(Console.Out, " ", display, 0);
}
///
/// Ecrit une hiérarchie d'objets dans la console
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
/// Chaine d'indentation à utiliser
public static void DumpHierarchy(this IEnumerable> nodes, string indent)
{
nodes.DumpHierarchy(Console.Out, indent, item => item.ToString(), 0);
}
///
/// Ecrit une hiérarchie d'objets dans la console
///
/// Type des objets de la hiérarchie
/// Collection de noeuds à afficher
public static void DumpHierarchy(this IEnumerable> nodes)
{
nodes.DumpHierarchy(Console.Out, " ", item => item.ToString(), 0);
}
private static void DumpHierarchy(this IEnumerable> nodes, TextWriter writer, string indent, Func display, int level)
{
foreach (var node in nodes)
{
for (int i = 0; i < level; i++) writer.Write(indent);
writer.WriteLine(display(node.Item));
if (node.Children != null)
node.Children.DumpHierarchy(writer, indent, display, level + 1);
}
}
#endregion
#region Flatten
///
/// Aplanit une hiérarchie d'objets de même type en énumérant tous les noeuds de la hiérarchie, dans l'ordre de parcours indiqué.
/// Les éléments renvoyés sont créés à partir d'un noeud et de son niveau dans la hiérarchie.
///
/// Type des éléments de la hiérarchie
/// Type des éléments de la séquence de résultat
/// Collection des éléments racines de la hiérarchie
/// Fonction qui renvoie les enfants d'un noeud de la hiérarchie
/// Mode de parcours de la hiérarchie
/// Fonction qui permet de renvoyer un résultat à partir d'un noeud et de son niveau dans la hiérarchie.
/// Une séquence contenant tous les noeuds de la hiérarchie
public static IEnumerable Flatten(this IEnumerable source, Func> childrenSelector, TreeTraversalMode traversalMode, Func resultSelector)
{
source.CheckArgumentNull("source");
childrenSelector.CheckArgumentNull("childrenSelector");
switch (traversalMode)
{
case TreeTraversalMode.DepthFirst:
return source.DepthFirstFlattenIterator(childrenSelector, resultSelector);
case TreeTraversalMode.BreadthFirst:
return source.BreadthFirstFlattenIterator(childrenSelector, resultSelector);
default:
throw new ArgumentOutOfRangeException("traversalMode");
}
}
///
/// Aplanit une hiérarchie d'objets de même type en énumérant tous les noeuds de la hiérarchie, dans l'ordre de parcours indiqué.
///
/// Type des éléments de la hiérarchie
/// Collection des éléments racines de la hiérarchie
/// Fonction qui renvoie les enfants d'un noeud de la hiérarchie
/// Mode de parcours de la hiérarchie
/// Une séquence contenant tous les noeuds de la hiérarchie
public static IEnumerable Flatten(this IEnumerable source, Func> childrenSelector, TreeTraversalMode traversalMode)
{
return Flatten(source, childrenSelector, traversalMode, (x, _) => x);
}
private static IEnumerable BreadthFirstFlattenIterator(this IEnumerable source, Func> childrenSelector, Func resultSelector)
{
var queue = new Queue>(source.Select(n => new NodeWithLevel(n, 0)));
while (queue.Count > 0)
{
var item = queue.Dequeue();
yield return resultSelector(item.Node, item.Level);
foreach (var child in childrenSelector(item.Node))
{
queue.Enqueue(new NodeWithLevel(child, item.Level + 1));
}
}
}
private static IEnumerable DepthFirstFlattenIterator(this IEnumerable source, Func> childrenSelector, Func resultSelector)
{
var list = new LinkedList>(source.Select(n => new NodeWithLevel(n, 0)));
while (list.Count > 0)
{
var current = list.First.Value;
list.RemoveFirst();
yield return resultSelector(current.Node, current.Level);
var llNode = list.First;
foreach (var child in childrenSelector(current.Node))
{
var newNode = new NodeWithLevel(child, current.Level + 1);
if (llNode != null)
list.AddBefore(llNode, newNode);
else
list.AddLast(newNode);
}
}
}
private class NodeWithLevel
{
private readonly TNode _node;
private readonly int _level;
public NodeWithLevel(TNode node, int level)
{
_node = node;
_level = level;
}
// ReSharper disable MemberHidesStaticFromOuterClass
public TNode Node
// ReSharper restore MemberHidesStaticFromOuterClass
{
get { return _node; }
}
public int Level
{
get { return _level; }
}
}
#endregion
}
}