Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This is called a "natural sort order", and is usually employed to sort items like those you have, like filenames and such.</p> <p>Here's a naive (in the sense that there are probably plenty of unicode-problems with it) implementation that seems to do the trick:</p> <p>You can copy the code below into <a href="http://www.linqpad.net/" rel="noreferrer">LINQPad</a> to execute it and test it.</p> <p>Basically the comparison algorithm will identify numbers inside the strings, and handle those by padding the shortest one with leading zeroes, so for instance the two strings <code>"Test123Abc"</code> and <code>"Test7X"</code> should be compared as though they were <code>"Test123Abc"</code> and <code>"Test007X"</code>, which should produce what you want.</p> <p>However, when I said "naive", I mean that I probably have tons of real unicode problems in here, like handling diacritics and multi-codepoint characters. If anyone can give a better implementation I would love to see it.</p> <p>Notes:</p> <ul> <li>The implementation does not actually parse the numbers, so arbitrarily long numbers should work just fine</li> <li>Since it doesn't actually parse the numbers as "numbers", floating point numbers will not be handled properly, "123.45" vs. "123.789" will be compared as "123.045" vs. "123.789", which is wrong.</li> </ul> <p>Code:</p> <pre><code>void Main() { List&lt;string&gt; input = new List&lt;string&gt; { "1", "5", "3", "6", "11", "9", "A1", "A0" }; var output = input.NaturalSort(); output.Dump(); } public static class Extensions { public static IEnumerable&lt;string&gt; NaturalSort( this IEnumerable&lt;string&gt; collection) { return NaturalSort(collection, CultureInfo.CurrentCulture); } public static IEnumerable&lt;string&gt; NaturalSort( this IEnumerable&lt;string&gt; collection, CultureInfo cultureInfo) { return collection.OrderBy(s =&gt; s, new NaturalComparer(cultureInfo)); } private class NaturalComparer : IComparer&lt;string&gt; { private readonly CultureInfo _CultureInfo; public NaturalComparer(CultureInfo cultureInfo) { _CultureInfo = cultureInfo; } public int Compare(string x, string y) { // simple cases if (x == y) // also handles null return 0; if (x == null) return -1; if (y == null) return +1; int ix = 0; int iy = 0; while (ix &lt; x.Length &amp;&amp; iy &lt; y.Length) { if (Char.IsDigit(x[ix]) &amp;&amp; Char.IsDigit(y[iy])) { // We found numbers, so grab both numbers int ix1 = ix++; int iy1 = iy++; while (ix &lt; x.Length &amp;&amp; Char.IsDigit(x[ix])) ix++; while (iy &lt; y.Length &amp;&amp; Char.IsDigit(y[iy])) iy++; string numberFromX = x.Substring(ix1, ix - ix1); string numberFromY = y.Substring(iy1, iy - iy1); // Pad them with 0's to have the same length int maxLength = Math.Max( numberFromX.Length, numberFromY.Length); numberFromX = numberFromX.PadLeft(maxLength, '0'); numberFromY = numberFromY.PadLeft(maxLength, '0'); int comparison = _CultureInfo .CompareInfo.Compare(numberFromX, numberFromY); if (comparison != 0) return comparison; } else { int comparison = _CultureInfo .CompareInfo.Compare(x, ix, 1, y, iy, 1); if (comparison != 0) return comparison; ix++; iy++; } } // we should not be here with no parts left, they're equal Debug.Assert(ix &lt; x.Length || iy &lt; y.Length); // we still got parts of x left, y comes first if (ix &lt; x.Length) return +1; // we still got parts of y left, x comes first return -1; } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload