using System.Collections;
using System.Collections.Generic;
public static void Main()
string[] namespaces = new[] {
"Root.Sub11.Sub21.Sub12",
"Root.Sub11.Sub21.Sub13.Sub14",
"Root.Sub11.Sub21.Sub13.Sub14",
"Root.Sub11.Sub21.Sub13.Sub14",
"Root.Sub11.Sub21.Sub13.Sub14.Sub47",
NamespacesToDirectoryStructure(namespaces.Select(n => n.Split(".")));
var sb = new StringBuilder();
foreach(var dir in typeNamespaceToPathMap.Values)
sb.AppendLine(new string(dir));
Console.WriteLine(sb.ToString());
private static readonly IDictionary<string[], char[]> typeNamespaceToPathMap = new Dictionary<string[], char[]>();
private static void NamespacesToDirectoryStructure(IEnumerable<string[]> namespaces)
IList<string[]> processedTypeNamespaces = new List<string[]>();
foreach (string[] segments in namespaces
.Distinct(new StringSequenceEqualityComparer())
char[] path = string.Join('.', segments).ToCharArray();
IList<int> sharedNamespacePortionIndices;
IList<string[]> sharedNamespacePortions = processedTypeNamespaces;
sharedNamespacePortionIndices = IndexOfAll(sharedNamespacePortions, segments, segment).ToList();
IList<int> indices = sharedNamespacePortionIndices;
IEnumerable<string[]> removedNamespaces = TakeWhere(sharedNamespacePortions, (i, x) => !indices.Contains(i));
if (removedNamespaces.Any())
int charIndex = segments.Take(segment).Select(s => s.Length).Aggregate(0, (x, y) => x + y + 1) - 1;
foreach (char[] value in typeNamespaceToPathMap
.Where(kvp => kvp.Value.Length > charIndex && (ReferenceEquals(kvp.Key, segments) || sharedNamespacePortions.Contains(kvp.Key)))
.Select(kvp => kvp.Value))
sharedNamespacePortions = TakeWhere(sharedNamespacePortions, (i, x) => sharedNamespacePortionIndices.Contains(i)).ToList();
} while (segment < segments.Length && sharedNamespacePortions.Count > 0);
processedTypeNamespaces.Add(segments);
typeNamespaceToPathMap.Add(segments, path);
private static IEnumerable<int> IndexOfAll(IList<string[]> collection, string[] test, int segment)
for (int index = 0; index < collection.Count; index++)
if (collection[index].Length > segment && String.Equals(test[segment], collection[index][segment], StringComparison.Ordinal))
private static IEnumerable<string[]> TakeWhere(IList<string[]> collection, Func<int, string[], bool> predicate)
for (int i = 0; i < collection.Count; i++)
if (predicate(i, collection[i]))
yield return collection[i];
private class StringSequenceEqualityComparer : IEqualityComparer<string[]>
public bool Equals(string[] x, string[] y)
if (x is null || x.Length != y?.Length)
Console.WriteLine(string.Join(".", x) + " <=> " + string.Join(".", y));
for(int i = 0; i < x.Length; i++)
if (!x[i].Equals(y[i], StringComparison.Ordinal))
public int GetHashCode(string[] obj) => obj.GetHashCode(true);
public static class CollectionHashing
public static int GetHashCode(this IList list, bool fromValues)
throw new ArgumentNullException(nameof(list));
return list.GetHashCode();
throw new ArgumentException(nameof(list), "List cannot be empty");
for (int i = 0; i < length; i++)
object value = list[i] ?? throw new NullReferenceException("Value cannot be null");
c = CombineHashCodes(c, value.GetHashCode());
internal static int CombineHashCodes(int h1, int h2)
return ((h1 << 5) + h1) ^ h2;