We can totally cheat here by PInvoking the Windows API function StrCmpLogicalW()
which does a natural sort-order comparison between two strings.
Then we can just use the built-in List.Sort(). No need to involve Linq at all:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Demo
{
class Program
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string lhs, string rhs);
private void run()
{
var list = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("1", "Day 1"),
new KeyValuePair<string, string>("2", "Day 11"),
new KeyValuePair<string, string>("4", "Day 5"),
new KeyValuePair<string, string>("6", "Day 13")
};
list.Sort((lhs, rhs) => StrCmpLogicalW(lhs.Value, rhs.Value));
foreach (var keyValuePair in list)
Console.WriteLine(keyValuePair);
}
static void Main(string[] args)
{
new Program().run();
}
}
}
I'm assuming that you do indeed want to sort on "Value" and not on "Key" and that the problem you have is that the string numbers aren't sorting in numerical order.
EDIT: Applying Jim Tollan's idea, you could create an extension method to hide the PInvoke away like so:
public static class ListExt
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string lhs, string rhs);
// Version for lists of any type.
public static void SortNatural<T>(this List<T> self, Func<T, string> stringSelector)
{
self.Sort((lhs, rhs) => StrCmpLogicalW(stringSelector(lhs), stringSelector(rhs)));
}
// Simpler version for List<string>
public static void SortNatural(this List<string> self)
{
self.Sort(StrCmpLogicalW);
}
}
Then the line that does the sorting of the list would look like this:
list.SortNatural(element => element.Value);