Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>There are several elements to the answer.</p> <p>The first part is being able to in-place sort a List using Sort() and a lambda comparison method. That's solved by using an extension method for IList and a helper "ComparisonDelegator" class. Combining those, it's possible to pass a lambda to List.Sort().</p> <p>The second part has been addressed in another post here (which I have upvoted) and the code from which I have shamelessly pasted into the AlphanumComparator class in this answer.</p> <p>(As a side note, I should point out that all the Linq examples posted elsewhere in this thread make a COPY of the list. This is fine for short lists, but if you have a long list it can cause performance problems. The solution presented here does NOT make a copy of the list.)</p> <p>Putting it all together, we get the following code, which outputs:</p> <pre><code>ID=2, Name=aaaa ID=3, Name=bbbb ID=1, Name=ccccc ID=5, Name=1 ID=7, Name=1a ID=6, Name=2 ID=4, Name=10 </code></pre> <p>And the full code sample (compilable as a console application):</p> <pre><code>using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Text; namespace Demo { public static class Program { public static void Main() { var list = new List&lt;Essay&gt; { new Essay {ID=1, Name="ccccc"}, new Essay {ID=2, Name="aaaa"}, new Essay {ID=3, Name="bbbb"}, new Essay {ID=4, Name="10"}, new Essay {ID=5, Name="1"}, new Essay {ID=6, Name="2"}, new Essay {ID=7, Name="1a"} }; var comp = new AlphanumComparator(); list.Sort((lhs, rhs) =&gt; comp.Compare(lhs.Name, rhs.Name)); foreach (var essay in list) { Console.WriteLine("ID={0}, Name={1}", essay.ID, essay.Name); } } } public class Essay { public int ID { get; set; } public string Name { get; set; } } /// &lt;summary&gt;Extensions for IList{T}&lt;/summary&gt; public static class ListExt { /// &lt;summary&gt; Sorts an IList{T} in place. &lt;/summary&gt; public static void Sort&lt;T&gt;(this IList&lt;T&gt; list, Comparison&lt;T&gt; comparison) { ArrayList.Adapter((IList)list).Sort(new ComparisonDelegator&lt;T&gt;(comparison)); } } /// &lt;summary&gt; /// Provides a mechanism for easily converting a Comparison&amp;lt;&amp;gt; delegate (or lambda) to an IComparer&amp;lt;&amp;gt;. /// This can be used for List.BinarySearch(), for example. /// &lt;/summary&gt; /// &lt;typeparam name="T"&gt;The type of items to be compared.&lt;/typeparam&gt; public sealed class ComparisonDelegator&lt;T&gt;: IComparer&lt;T&gt;, IComparer { /// &lt;summary&gt;Create from a Comparison&amp;lt;&amp;gt; delegate.&lt;/summary&gt; /// &lt;param name="comparison"&gt;A Comparison&amp;lt;&amp;gt; delegate.&lt;/param&gt; public ComparisonDelegator(Comparison&lt;T&gt; comparison) { this._comparison = comparison; } /// &lt;summary&gt;Implements the IComparer.Compare() method.&lt;/summary&gt; public int Compare(T x, T y) { return _comparison(x, y); } /// &lt;summary&gt;Implements the IComparer.Compare() method.&lt;/summary&gt; public int Compare(object x, object y) { return _comparison((T)x, (T)y); } /// &lt;summary&gt;Used to store the Comparison delegate.&lt;/summary&gt; private readonly Comparison&lt;T&gt; _comparison; } /// &lt;summary&gt; /// Special class to sort strings "naturally", /// but to place non-numeric items *before* numeric items. /// &lt;/summary&gt; public class AlphanumComparator : IComparer { private enum ChunkType {Alphanumeric, Numeric}; private bool InChunk(char ch, char otherCh) { ChunkType type = ChunkType.Alphanumeric; if (char.IsDigit(otherCh)) { type = ChunkType.Numeric; } if ((type == ChunkType.Alphanumeric &amp;&amp; char.IsDigit(ch)) || (type == ChunkType.Numeric &amp;&amp; !char.IsDigit(ch))) { return false; } return true; } public int Compare(object x, object y) { String s1 = x as string; String s2 = y as string; if (s1 == null || s2 == null) { return 0; } int thisMarker = 0, thisNumericChunk = 0; int thatMarker = 0, thatNumericChunk = 0; while ((thisMarker &lt; s1.Length) || (thatMarker &lt; s2.Length)) { if (thisMarker &gt;= s1.Length) { return -1; } else if (thatMarker &gt;= s2.Length) { return 1; } char thisCh = s1[thisMarker]; char thatCh = s2[thatMarker]; StringBuilder thisChunk = new StringBuilder(); StringBuilder thatChunk = new StringBuilder(); while ((thisMarker &lt; s1.Length) &amp;&amp; (thisChunk.Length==0 ||InChunk(thisCh, thisChunk[0]))) { thisChunk.Append(thisCh); thisMarker++; if (thisMarker &lt; s1.Length) { thisCh = s1[thisMarker]; } } while ((thatMarker &lt; s2.Length) &amp;&amp; (thatChunk.Length==0 ||InChunk(thatCh, thatChunk[0]))) { thatChunk.Append(thatCh); thatMarker++; if (thatMarker &lt; s2.Length) { thatCh = s2[thatMarker]; } } int result = 0; // If both chunks contain numeric characters, sort them numerically if (char.IsDigit(thisChunk[0]) &amp;&amp; char.IsDigit(thatChunk[0])) { thisNumericChunk = Convert.ToInt32(thisChunk.ToString()); thatNumericChunk = Convert.ToInt32(thatChunk.ToString()); if (thisNumericChunk &lt; thatNumericChunk) { result = -1; } if (thisNumericChunk &gt; thatNumericChunk) { result = 1; } } else if (char.IsDigit(thisChunk[0]) &amp;&amp; !char.IsDigit(thatChunk[0])) { return 1; // Ensure that non-numeric sorts before numeric. } else if (!char.IsDigit(thisChunk[0]) &amp;&amp; char.IsDigit(thatChunk[0])) { return -1; // Ensure that non-numeric sorts before numeric. } else { result = thisChunk.ToString().CompareTo(thatChunk.ToString()); } if (result != 0) { return result; } } return 0; } } } </code></pre>
 

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