using System.Collections.Generic;
public string Name { get; set; }
public string[] Evolutions { get; set; }
public static void Main()
new Version { Name = "v0", Evolutions = new[] { "Add foo 8", "Add bar 9" }},
new Version { Name = "v2", Evolutions = new[] { "Add foo 0", "Add bar 1" }},
new Version { Name = "v1", Evolutions = new[] { "Add foo 6", "Add bar 7" }},
new Version { Name = "v1.1", Evolutions = new[] { "Add foo 4", "Add bar 5" }},
new Version { Name = "v1.2-alpha", Evolutions = new[] { "Add foo 2", "Add bar 3" }}
var cleanedVersions = OrderByVersion(versions).ToArray();
Console.WriteLine(WriteVersions(cleanedVersions));
private static IEnumerable<Version> OrderByVersion(IEnumerable<Version> versions)
from v in versions.Select(v => new
SemVer = ExtractSemVer(v.Name)
.OrderByDescending(v => v.SemVer[0])
.ThenByDescending(v => v.SemVer[1])
.ThenByDescending(v => v.SemVer[2])
select new Version { Name = v.Name, Evolutions = v.Evolutions };
private static string[] ExtractSemVer(string version)
var semver = new string(version.Where(c => c.Equals('.') || Char.IsDigit(c)).ToArray());
return !semver.Contains('.')
? new[] { semver, String.Empty, String.Empty }
semver.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries))
public static string WriteVersions(IList<Version> versions)
Func<bool, string> lineOrEmpty = b => b ? Environment.NewLine : String.Empty;
Func<int, int, bool> isLimit = (n, limit) => n + 1 < limit;
Func<int, string> line = n => lineOrEmpty(isLimit(n, versions.Count));
var stringBuilder = new StringBuilder("## Evolutions");
stringBuilder.AppendLine()
foreach (var version in versions)
stringBuilder.AppendFormat("### {0}", version.Name)
var length = version.Evolutions.Length;
foreach (var evolution in version.Evolutions)
stringBuilder.AppendFormat("* {0}", evolution)
stringBuilder.AppendLine();
return stringBuilder.ToString();