Alphanumeric/natural sorting in C# using IComparer

These snippets illustrate the natural sorting order. Suppose you’re using numbers within your file names. For example:

  • file1.png
  • file2.png
  • file10.png
  • file11.png
  • file12.png

You’d expect to see them in the above order. However, ¬†they are (by default) sorted as follows.

  • file1.png
  • file10.png
  • file11.png
  • file12.png
  • file2.png

The following class implements IComparer and performs natural sorting on a collection (major credits to: http://www.dotnetperls.com/alphanumeric-sorting).

AlphanumComparatorFast class

using System;
using System.Collections;

namespace NaturalSorting
{
	public class AlphanumComparatorFast : IComparer
	{
		public int Compare(object x, object y)
		{
			string s1 = x as string;
			if (s1 == null)
			{
				return 0;
			}
			string s2 = y as string;
			if (s2 == null)
			{
				return 0;
			}

			int len1 = s1.Length;
			int len2 = s2.Length;
			int marker1 = 0;
			int marker2 = 0;

			// Walk through two the strings with two markers.
			while (marker1 < len1 && marker2 < len2)
			{
				char ch1 = s1[marker1];
				char ch2 = s2[marker2];

				// Some buffers we can build up characters in for each chunk.
				char[] space1 = new char[len1];
				int loc1 = 0;
				char[] 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 = s1[marker1];
					}
					else
					{
						break;
					}
				} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

				do
				{
					space2[loc2++] = ch2;
					marker2++;

					if (marker2 < len2)
					{
						ch2 = s2[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.
				string str1 = new string(space1);
				string 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 = str1.CompareTo(str2);
				}

				if (result != 0)
				{
					return result;
				}
			}
			return len1 - len2;
		}
	}
}

The main program

using System;

namespace NaturalSorting
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			string[] files = new string[] {
				"file1.png",
				"file2.png",
				"file12.png",
				"file11.png",
				"file10.png"
			};

			// Regular
			Array.Sort(files);
			Console.WriteLine(files.ToConcatenatedString());

			// Natural
			Array.Sort(files, new AlphanumComparatorFast());
			Console.WriteLine (files.ToConcatenatedString());

			Console.ReadLine();
		}

	}
}

Array extensions

using System;

namespace NaturalSorting
{
	public static class ArrayExtensions
	{
		public static string ToConcatenatedString(this string[] array){
			string strItems = string.Empty;

			foreach (var item in array) {
				strItems += string.Format("{0} ",item);
			}

			return strItems;
		}
	}
}

Output

file1.png file10.png file11.png file12.png file2.png
file1.png file2.png file10.png file11.png file12.png

You may download the solution file from here.

This entry was posted in C# and tagged , , , , , , , , . Bookmark the permalink. Trackbacks are closed, but you can post a comment.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Why ask?