Solved

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

Posted on 2014-12-23
4
186 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

Suggested Solutions

Title # Comments Views Activity
Diagnostics with Net and Net.Sockets 2 29
Set value of dynamically created checkboxlist 2 34
Simple Injector with Web Service 4 42
VB.NET 2008 Publish Error 2 27
We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
In my previous two articles we discussed Binary Serialization (http://www.experts-exchange.com/A_4362.html) and XML Serialization (http://www.experts-exchange.com/A_4425.html). In this article we will try to know more about SOAP (Simple Object Acces…
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…

809 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