How to sort depended objects by dependency
I have a collection:
List<VPair<Item, List<Item>> dependencyHierarchy;
The first item in pair is some object (item) and the second one is a collection of the same type objects that the first one depends on. I want to get a List<Item>
in order of dependency, so there are not items that depend on the first element and so on (no cycled dependency!).
Input:
Item4 depends on Item3 and Item5 Item3 depends on Item1 Item1 does not depend on any one Item2 depends on Item4 Item5 does not depend on any one
Result:
Item1 Item5 Item3 Item4 Item2
Thank you.
SOLUTION:
Topological Sorting (thanks to Loïc Février for idea)
and
example on C#, example on Java (thanks to xcud for great examples)
Solution 1:
Having struggled with this for a while, here's my attempt at a Linq style TSort extension method:
public static IEnumerable<T> TSort<T>( this IEnumerable<T> source, Func<T, IEnumerable<T>> dependencies, bool throwOnCycle = false )
{
var sorted = new List<T>();
var visited = new HashSet<T>();
foreach( var item in source )
Visit( item, visited, sorted, dependencies, throwOnCycle );
return sorted;
}
private static void Visit<T>( T item, HashSet<T> visited, List<T> sorted, Func<T, IEnumerable<T>> dependencies, bool throwOnCycle )
{
if( !visited.Contains( item ) )
{
visited.Add( item );
foreach( var dep in dependencies( item ) )
Visit( dep, visited, sorted, dependencies, throwOnCycle );
sorted.Add( item );
}
else
{
if( throwOnCycle && !sorted.Contains( item ) )
throw new Exception( "Cyclic dependency found" );
}
}
Solution 2:
Perfect example to use a topological sort :
http://en.wikipedia.org/wiki/Topological_sorting
It will give you exactly what you need.
Solution 3:
There's a nuget for that.
For those of us who prefer not to re-invent the wheel: use nuget to install the QuickGraph .NET library, which includes multiple graph algorithms including topological sort.
To use it, you need to create an instance of AdjacencyGraph<,>
such as AdjacencyGraph<String, SEdge<String>>
. Then, if you include the appropriate extensions:
using QuickGraph.Algorithms;
You can call:
var sorted = myGraph.TopologicalSort();
To get a list of sorted nodes.
Solution 4:
I liked DMM's answer, but it assumes that the input nodes are leaves (which may or may not be what is expected).
I am posting an alternate solution using LINQ that does not make this assumption. In addition, this solution uses yield return
to be able to quickly return the leaves (using e.g. TakeWhile
).
public static IEnumerable<T> TopologicalSort<T>(this IEnumerable<T> nodes,
Func<T, IEnumerable<T>> connected)
{
var elems = nodes.ToDictionary(node => node,
node => new HashSet<T>(connected(node)));
while (elems.Count > 0)
{
var elem = elems.FirstOrDefault(x => x.Value.Count == 0);
if (elem.Key == null)
{
throw new ArgumentException("Cyclic connections are not allowed");
}
elems.Remove(elem.Key);
foreach (var selem in elems)
{
selem.Value.Remove(elem.Key);
}
yield return elem.Key;
}
}