Solved

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

Posted on 2014-12-23
4
199 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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 34

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 34

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

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

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

Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
In this video, viewers will be given step by step instructions on adjusting mouse, pointer and cursor visibility in Microsoft Windows 10. The video seeks to educate those who are struggling with the new Windows 10 Graphical User Interface. Change Cu…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…

691 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