Solved

Generic collection Sort doesn't work when there is training number in the data to sort.

Posted on 2014-12-23
4
187 Views
Last Modified: 2014-12-24
Hi, I'm using VS2012.
I have a collection of List<UserPrincipal>.  Using the following code to sort based on the userid works until the userid ends with numbers larger than 9.  UserId1, UserId2.... UserId9, will sort correctly.  UserId10, UserId11...   will fall right er under UserId1.  UserId20, UserId21...  will fall under UserId2..


users.Sort(delegate(UserPrincipal a, UserPrincipal b) { return string.Compare(a.SamAccountName, b.SamAccountName); });

How can I fix this?  Thank you.
0
Comment
Question by:lapucca
  • 2
4 Comments
 
LVL 63

Assisted Solution

by:Fernando Soto
Fernando Soto earned 100 total points
ID: 40515491
Numeric characters in a string do not sort as you may think. If you want them to sort like alpha characters zero fill the beginning numeric sequence. So lets say that the largest numeric value is 999 then use userID's like this:

UserId001
UserId002
...
UserId010
...
UserId060
..
UserId100

This pattern will handle digits from 000 to 999.
0
 
LVL 33

Accepted Solution

by:
it_saige earned 400 total points
ID: 40515582
Just to add another option (although I see nothing wrong with Raheman's implementation): Natural Sort Comparer

Implementation -
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Text.RegularExpressions;

namespace EE_Q28585582
{
	class Program
	{
		private static List<UserPrincipal> users = new List<UserPrincipal>();

		static void Main(string[] args)
		{
			PrincipalContext context = new PrincipalContext(ContextType.Domain);
			UserPrincipal user = new UserPrincipal(context);
			PrincipalSearcher searcher = new PrincipalSearcher(user);
			foreach (Principal result in searcher.FindAll())
			{
				UserPrincipal upe = result as UserPrincipal;
				if (upe != null)
					users.Add(upe);
			}

			using (NaturalComparer comparer = new NaturalComparer())
				users.Sort((a, b) => comparer.Compare(a.SamAccountName, b.SamAccountName));

			foreach (UserPrincipal up in users)
				Console.WriteLine("User: {0}; SamAccount: {1}", up.Name, up.SamAccountName);

			Console.ReadLine();
		}
	}

	public class NaturalComparer : Comparer<string>, IDisposable
	{
		private Dictionary<string, string[]> table;

		public NaturalComparer()
		{
			table = new Dictionary<string, string[]>();
		}

		public void Dispose()
		{
			table.Clear();
			table = null;
		}

		public override int Compare(string x, string y)
		{
			if (x == y)
			{
				return 0;
			}
			string[] x1, y1;
			if (!table.TryGetValue(x, out x1))
			{
				x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)");
				table.Add(x, x1);
			}
			if (!table.TryGetValue(y, out y1))
			{
				y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)");
				table.Add(y, y1);
			}

			for (int i = 0; i < x1.Length && i < y1.Length; i++)
			{
				if (x1[i] != y1[i])
					return PartCompare(x1[i], y1[i]);
			}

			if (y1.Length > x1.Length)
				return 1;
			else if (x1.Length > y1.Length)
				return -1;
			else
				return 0;
		}

		private static int PartCompare(string left, string right)
		{
			int x, y;
			if (!int.TryParse(left, out x))
				return left.CompareTo(right);

			if (!int.TryParse(right, out y))
				return left.CompareTo(right);

			return x.CompareTo(y);
		}
	}
}

Open in new window


ADUC -Capture.JPG
Output -Capture.JPG
-saige-
0
 
LVL 33

Expert Comment

by:it_saige
ID: 40515610
Just for completeness and to test I implemented Raheman's recommendation:
using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Text.RegularExpressions;

namespace EE_Q28585582
{
	class Program
	{
		private static List<UserPrincipal> users = new List<UserPrincipal>();

		static void Main(string[] args)
		{
			PrincipalContext context = new PrincipalContext(ContextType.Domain);
			UserPrincipal user = new UserPrincipal(context);
			PrincipalSearcher searcher = new PrincipalSearcher(user);
			foreach (Principal result in searcher.FindAll())
			{
				UserPrincipal upe = result as UserPrincipal;
				if (upe != null && upe.Name.Contains("Natural"))
					users.Add(upe);
			}

			Console.WriteLine("Using Justin Jones' Natural Sort Comparer:");
			using (NaturalComparer comparer = new NaturalComparer())
				users.Sort((a, b) => comparer.Compare(a.SamAccountName, b.SamAccountName));

			foreach (UserPrincipal up in users)
				Console.WriteLine("User: {0}; SamAccount: {1}", up.Name, up.SamAccountName);

			users = new List<UserPrincipal>();
			foreach (Principal result in searcher.FindAll())
			{
				UserPrincipal upe = result as UserPrincipal;
				if (upe != null && upe.Name.Contains("Natural"))
					users.Add(upe);
			}

			Console.WriteLine();
			Console.WriteLine("Using Raheman's comparer:");
			users.Sort((a, b) => CompareUsers(a.SamAccountName, b.SamAccountName));
			foreach (UserPrincipal up in users)
				Console.WriteLine("User: {0}; SamAccount: {1}", up.Name, up.SamAccountName);

			Console.ReadLine();
		}

		public static int CompareUsers(string x, string y)
		{
			var regex = new Regex("^(d+)");

			// run the regex on both strings
			var xRegexResult = regex.Match(x);
			var yRegexResult = regex.Match(y);

			// check if they are both numbers
			if (xRegexResult.Success && yRegexResult.Success)
				return int.Parse(xRegexResult.Groups[1].Value).CompareTo(int.Parse(yRegexResult.Groups[1].Value));

			// otherwise return as string comparison
			return x.CompareTo(y);
		}
	}

	public class NaturalComparer : Comparer<string>, IDisposable
	{
		private Dictionary<string, string[]> table;

		public NaturalComparer()
		{
			table = new Dictionary<string, string[]>();
		}

		public void Dispose()
		{
			table.Clear();
			table = null;
		}

		public override int Compare(string x, string y)
		{
			if (x == y)
			{
				return 0;
			}
			string[] x1, y1;
			if (!table.TryGetValue(x, out x1))
			{
				x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)");
				table.Add(x, x1);
			}
			if (!table.TryGetValue(y, out y1))
			{
				y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)");
				table.Add(y, y1);
			}

			for (int i = 0; i < x1.Length && i < y1.Length; i++)
			{
				if (x1[i] != y1[i])
					return PartCompare(x1[i], y1[i]);
			}

			if (y1.Length > x1.Length)
				return 1;
			else if (x1.Length > y1.Length)
				return -1;
			else
				return 0;
		}

		private static int PartCompare(string left, string right)
		{
			int x, y;
			if (!int.TryParse(left, out x))
				return left.CompareTo(right);

			if (!int.TryParse(right, out y))
				return left.CompareTo(right);

			return x.CompareTo(y);
		}
	}
}

Open in new window


ADUC -Capture.JPG
Results -Capture.JPG
Seeing this, I would recommend Justin Jones' Natural Sort Comparer.

-saige-
0
 

Author Closing Comment

by:lapucca
ID: 40516946
Thank you both
0

Featured Post

Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…
In an interesting question (https://www.experts-exchange.com/questions/29008360/) here at Experts Exchange, a member asked how to split a single image into multiple images. The primary usage for this is to place many photographs on a flatbed scanner…

790 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question