Task: Topological sort



C#: namespace Algorithms
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public class TopologicalSorter<ValueType>
    {
        private class Relations
        {
            public int Dependencies = 0;
            public HashSet<ValueType> Dependents = new HashSet<ValueType>();
        }

        private Dictionary<ValueType, Relations> _map = new Dictionary<ValueType, Relations>();

        public void Add(ValueType obj)
        {
            if (!_map.ContainsKey(obj)) _map.Add(obj, new Relations());
        }

        public void Add(ValueType obj, ValueType dependency)
        {
            if (dependency.Equals(obj)) return;

            if (!_map.ContainsKey(dependency)) _map.Add(dependency, new Relations());

            var dependents = _map[dependency].Dependents;

            if (!dependents.Contains(obj))
            {
                dependents.Add(obj);

                if (!_map.ContainsKey(obj)) _map.Add(obj, new Relations());

                ++_map[obj].Dependencies;
            }
        }

        public void Add(ValueType obj, IEnumerable<ValueType> dependencies)
        {
            foreach (var dependency in dependencies) Add(obj, dependency);
        }

        public void Add(ValueType obj, params ValueType[] dependencies)
        {
            Add(obj, dependencies as IEnumerable<ValueType>);
        }

        public Tuple<IEnumerable<ValueType>, IEnumerable<ValueType>> Sort()
        {
            List<ValueType> sorted = new List<ValueType>(), cycled = new List<ValueType>();
            var map = _map.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            sorted.AddRange(map.Where(kvp => kvp.Value.Dependencies == 0).Select(kvp => kvp.Key));

            for (int idx = 0; idx < sorted.Count; ++idx) sorted.AddRange(map[sorted[idx]].Dependents.Where(k => --map[k].Dependencies == 0));

            cycled.AddRange(map.Where(kvp => kvp.Value.Dependencies != 0).Select(kvp => kvp.Key));

            return new Tuple<IEnumerable<ValueType>, IEnumerable<ValueType>>(sorted, cycled);
        }

        public void Clear()
        {
            _map.Clear();
        }
    }

}



namespace ExampleApplication
{
    using Algorithms;
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public class Task
    {
        public string Message;
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<Task> tasks = new List<Task>
            {
                new Task{ Message = "A - depends on B and C" },    
                new Task{ Message = "B - depends on none" },       
                new Task{ Message = "C - depends on D and E" },    
                new Task{ Message = "D - depends on none" },       
                new Task{ Message = "E - depends on F, G and H" }, 
                new Task{ Message = "F - depends on I" },          
                new Task{ Message = "G - depends on none" },       
                new Task{ Message = "H - depends on none" },       
                new Task{ Message = "I - depends on none" },       
            };

            TopologicalSorter<Task> resolver = new TopologicalSorter<Task>();

            
            resolver.Add(tasks[0], new[] { tasks[1], tasks[2] });
            
            resolver.Add(tasks[2], new[] { tasks[3], tasks[4] });
            
            resolver.Add(tasks[4], tasks[5], tasks[6], tasks[7]);
            resolver.Add(tasks[5], tasks[8]);
            
            

            

            var result = resolver.Sort();
            var sorted = result.Item1;
            var cycled = result.Item2;

            if (!cycled.Any())
            {
                foreach (var d in sorted) Console.WriteLine(d.Message);
            }
            else
            {
                Console.Write("Cycled dependencies detected: ");

                foreach (var d in cycled) Console.Write($"{d.Message[0]} ");

                Console.WriteLine();
            }

            Console.WriteLine("exiting...");
        }
    }
}






VB: class topological
	dim dictDependencies
	dim dictReported
	dim depth
	
	sub class_initialize
		set dictDependencies = createobject("Scripting.Dictionary")
		set dictReported = createobject("Scripting.Dictionary")
		depth = 0
	end sub
	
	sub reset
		dictReported.removeall
	end sub
	
	property let dependencies( s )
		
		dim i, j ,k
		dim aList
		dim dep
		dim a1
		aList = Split( s, vbNewLine )
		
		do while aList( UBound( aList ) ) = vbnullstring
			redim preserve aList( UBound( aList ) - 1 )
		loop

		for i = lbound( aList ) to ubound( aList )
			aList( i ) = Split( aList( i ), vbTab, 2 )
			a1 = Split( aList( i )( 1 ), " " )
			k = 0
			for j = lbound( a1) to ubound(a1)
				if a1(j) <> aList(i)(0) then
					a1(k) = a1(j)
					k = k + 1
				end if
			next
			redim preserve a1(k-1)
			aList(i)(1) = a1
		next
		for i = lbound( aList ) to ubound( aList )
			dep = aList(i)(0)
			if not dictDependencies.Exists( dep ) then
				dictDependencies.add dep, aList(i)(1)
			end if
		next
		
	end property
	
	sub resolve( s )
		dim i 
		dim deps
		
		depth = depth + 1
		if dictDependencies.Exists(s) then
			deps = dictDependencies(s)
			for i = lbound(deps) to ubound(deps)
				resolve deps(i)
			next
		end if
		if not seen(s) then
			wscript.echo s
			see s
		end if
		depth = depth - 1
	end sub

	function seen( key )
		seen = dictReported.Exists( key )
	end function
	
	sub see( key )
		dictReported.add key, ""
	end sub
	
	property get keys
		keys = dictDependencies.keys
	end property
end class