Skip to content

Instantly share code, notes, and snippets.

@ngbrown
Created October 5, 2012 20:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ngbrown/3842065 to your computer and use it in GitHub Desktop.
Save ngbrown/3842065 to your computer and use it in GitHub Desktop.
Alphanumeric compariter using Linq and an EnumerableComparer.
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);
}
}
}
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);
}
}
}
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