-
-
Save flashfoxter/fa013a8f79cc00151a22 to your computer and use it in GitHub Desktop.
Alphanumeric compariter using Linq and an EnumerableComparer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Utilities | |
{ | |
using System.Collections; | |
using System.Collections.Generic; | |
/// <remarks> | |
/// From http://www.dotnetperls.com/alphanumeric-sorting | |
/// </remarks> | |
public class AlphanumComparatorFast : IComparer<string>, IComparer | |
{ | |
public int Compare(string x, string y) | |
{ | |
if (x == null) | |
{ | |
return 0; | |
} | |
if (y == null) | |
{ | |
return 0; | |
} | |
int len1 = x.Length; | |
int len2 = y.Length; | |
int marker1 = 0; | |
int marker2 = 0; | |
// Walk through two the strings with two markers. | |
while (marker1 < len1 && marker2 < len2) | |
{ | |
char ch1 = x[marker1]; | |
char ch2 = y[marker2]; | |
// Some buffers we can build up characters in for each chunk. | |
var space1 = new char[len1]; | |
int loc1 = 0; | |
var space2 = new char[len2]; | |
int loc2 = 0; | |
// Walk through all following characters that are digits or | |
// characters in BOTH strings starting at the appropriate marker. | |
// Collect char arrays. | |
do | |
{ | |
space1[loc1++] = ch1; | |
marker1++; | |
if (marker1 < len1) | |
{ | |
ch1 = x[marker1]; | |
} | |
else | |
{ | |
break; | |
} | |
} while (char.IsDigit(ch1) == char.IsDigit(space1[0])); | |
do | |
{ | |
space2[loc2++] = ch2; | |
marker2++; | |
if (marker2 < len2) | |
{ | |
ch2 = y[marker2]; | |
} | |
else | |
{ | |
break; | |
} | |
} while (char.IsDigit(ch2) == char.IsDigit(space2[0])); | |
// If we have collected numbers, compare them numerically. | |
// Otherwise, if we have strings, compare them alphabetically. | |
var str1 = new string(space1); | |
var str2 = new string(space2); | |
int result; | |
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0])) | |
{ | |
int thisNumericChunk = int.Parse(str1); | |
int thatNumericChunk = int.Parse(str2); | |
result = thisNumericChunk.CompareTo(thatNumericChunk); | |
} | |
else | |
{ | |
result = System.String.Compare(str1.Replace(" ", ""), str2.Replace(" ", ""), System.StringComparison.InvariantCultureIgnoreCase); | |
} | |
if (result != 0) | |
{ | |
return result; | |
} | |
} | |
return len1 - len2; | |
} | |
public int Compare(object x, object y) | |
{ | |
return this.Compare(x as string, y as string); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Utilities | |
{ | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Text.RegularExpressions; | |
using System.Linq; | |
/// <summary> | |
/// Compares two sequences. | |
/// </summary> | |
/// <typeparam name="T">Type of item in the sequences.</typeparam> | |
/// <remarks> | |
/// Compares elements from the two input sequences in turn. If we | |
/// run out of list before finding unequal elements, then the shorter | |
/// list is deemed to be the lesser list.<br/> | |
/// From http://www.interact-sw.co.uk/iangblog/2007/12/13/natural-sorting | |
/// </remarks> | |
/// <example> | |
/// <code lang="cs"> | |
/// <![CDATA[ | |
/// string[] testItems = { "z24", "z2", "z15", "z1", | |
/// "z3", "z20", "z5", "z11", | |
/// "z 21", "z22" }; | |
/// | |
/// Func<string, object> convert = str => | |
/// { try { return int.Parse(str); } | |
/// catch { return str; } }; | |
/// var sorted = testItems.OrderBy( | |
/// str => Regex.Split(str.Replace(" ", ""), "([0-9]+)").Select(convert), | |
/// new EnumerableComparer<object>()); | |
/// ]]> | |
/// </code> | |
/// </example> | |
public class EnumerableComparer<T> : IComparer<IEnumerable<T>> | |
{ | |
/// <summary> | |
/// Create a sequence comparer using the default comparer for T. | |
/// </summary> | |
public EnumerableComparer() | |
{ | |
comp = Comparer<T>.Default; | |
} | |
/// <summary> | |
/// Create a sequence comparer, using the specified item comparer | |
/// for T. | |
/// </summary> | |
/// <param name="comparer">Comparer for comparing each pair of | |
/// items from the sequences.</param> | |
public EnumerableComparer(IComparer<T> comparer) | |
{ | |
comp = comparer; | |
} | |
/// <summary> | |
/// Object used for comparing each element. | |
/// </summary> | |
private readonly IComparer<T> comp; | |
/// <summary> | |
/// Compare two sequences of T. | |
/// </summary> | |
/// <param name="x">First sequence.</param> | |
/// <param name="y">Second sequence.</param> | |
public int Compare(IEnumerable<T> x, IEnumerable<T> y) | |
{ | |
using (IEnumerator<T> leftIt = x.GetEnumerator()) | |
using (IEnumerator<T> rightIt = y.GetEnumerator()) | |
{ | |
while (true) | |
{ | |
bool left = leftIt.MoveNext(); | |
bool right = rightIt.MoveNext(); | |
if (!(left || right)) return 0; | |
if (!left) return -1; | |
if (!right) return 1; | |
int itemResult = comp.Compare(leftIt.Current, rightIt.Current); | |
if (itemResult != 0) return itemResult; | |
} | |
} | |
} | |
} | |
public class AlphanumericComparer : IComparer<string>, IComparer | |
{ | |
static readonly Regex RegexSplit = new Regex(@"([0-9]+)", RegexOptions.Compiled); | |
private static readonly EnumerableComparer<object> EnumerableComparer = new EnumerableComparer<object>(); | |
private static object Convert(string str) | |
{ | |
int intValue; | |
if (int.TryParse(str, out intValue)) | |
{ | |
return intValue; | |
} | |
return str; | |
} | |
public int Compare(string x, string y) | |
{ | |
if (x == null || y == null) return 0; | |
return EnumerableComparer.Compare( | |
RegexSplit.Split(x.Replace(" ", "")).Select(Convert), | |
RegexSplit.Split(y.Replace(" ", "")).Select(Convert)); | |
} | |
public int Compare(object x, object y) | |
{ | |
return this.Compare(x as string, y as string); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Utilities | |
{ | |
using System; | |
using System.Collections; | |
public class SelectableComparer<T> : IComparer where T : class | |
{ | |
private readonly IComparer comparer; | |
private readonly Func<T, string> converter; | |
public SelectableComparer(IComparer comparer, Func<T, string> converter) | |
{ | |
this.comparer = comparer; | |
this.converter = converter; | |
} | |
public int Compare(object x, object y) | |
{ | |
var xt = x as T; | |
var yt = y as T; | |
if (xt == null || yt == null) return 0; | |
return this.comparer.Compare(this.converter(xt), this.converter(yt)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment