For a small personal project, I am looking to convert an object to an ordered by SemVer Markdown list. You can find the original code for the sort logic here and here for the writing into Markdown logic. I merged them to a full working snippet. I am using C# 5 in .NET 4.5.1.
Basically, the goal is to transform this:
var versions = new[]
{
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" }}
};
into that:
## Evolutions
### v2
* Add foo 0
* Add bar 1
### v1.2-alpha
* Add foo 2
* Add bar 3
### v1.1
* Add foo 4
* Add bar 5
### v1
* Add foo 6
* Add bar 7
### v0
* Add foo 8
* Add bar 9
I wrote the code to do it but I am not very proud of it. It smells not right. I am sure I can clear it and use betters way to achieve what I want. You can try the whole program in .NETFiddle.
What do you think of it? How can I improve it?
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
public class Program
{
public class Version
{
public string Name { get; set; }
public string[] Evolutions { get; set; }
}
public static void Main()
{
var versions = new[]
{
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));
}
/// <summary>
/// Order by version an enumerable of Version
/// </summary>
/// <param name="versions">An enumerable of Version to sort</param>
/// <returns>An enumerable of Version sorted</returns>
/// <example>
/// [[1, 2], [1, 1], [2, 0]] will give [[2, 0], [1, 2], [1, 1]]
/// </example>
private static IEnumerable<Version> OrderByVersion(IEnumerable<Version> versions)
{
return
from v in versions.Select(v => new
{
v.Name,
v.Evolutions,
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 };
}
/// <summary>
/// Extract SemVer format from string into an array
/// </summary>
/// <param name="version">A version in the SemVer format</param>
/// <returns>An array of string composed by SemVer format</returns>
/// <example>
/// v0 will give [0, "", ""]
/// v1.2.3-foobar will give [1, 2, 3]
/// </example>
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 }
: new List<string>(
semver.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries))
{
String.Empty,
String.Empty
}.ToArray();
}
/// <summary>
/// Write a Markdown list in a string form from a list of versions
/// </summary>
/// <param name="versions">A list of version to write in Markdown</param>
/// <returns>A Markdown list in a string form</returns>
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" + Environment.NewLine + Environment.NewLine);
for (var i = 0; i < versions.Count; i++)
{
stringBuilder.AppendFormat("### {0}{1}{1}",
versions[i].Name,
Environment.NewLine);
for (int index = 0; index < versions[i].Evolutions.Length; index++)
{
var length = versions[i].Evolutions.Length;
stringBuilder.AppendFormat("* {0}{1}",
versions[i].Evolutions[index],
lineOrEmpty(!String.IsNullOrEmpty(line(i)) || isLimit(index, length)));
}
stringBuilder.Append(line(i));
}
return stringBuilder.ToString();
}
}
The class is self-contained and documented but if you want more information, feel free to ask for it.