Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

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

Posted on 2014-12-23
4
Medium Priority
?
230 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 64

Assisted Solution

by:Fernando Soto
Fernando Soto earned 400 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 35

Accepted Solution

by:
it_saige earned 1600 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 35

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

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

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:…
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…
This Micro Tutorial will teach you how to add a cinematic look to any film or video out there. There are very few simple steps that you will follow to do so. This will be demonstrated using Adobe Premiere Pro CS6.
The Relationships Diagram is a good way to get an overall view of what a database is keeping track of. It is also where relationships are defined. A relationship specifies how two tables connect to each other. As you build tables in Microsoft Ac…
Suggested Courses

580 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