views:

390

answers:

6

Hey there.

This has been bugging me for some time now. I've tried several approaches and none have worked properly.

I'm writing and IRC client and am trying to sort out the list of usernames (which needs to be sorted by a users' access level in the current channel).

This is easy enough. Trouble is, this list needs to added to whenever a user joins or leaves the channel so their username must be removed the list when the leave and re-added in the correct position when they rejoin.

Each users' access level is signified by a single character at the start of each username. These characters are reserved, so there's no potential problem of a name starting with one of the symbols. The symbols from highest to lowest (in the order I need to sort them) are: ~ & @ % +

Users without any sort of access have no symbol before their username. They should be at the bottom of the list.

For example: the unsorted array could contain the following: ~user1 ~user84 @user3 &user8 +user39 user002 user2838 %user29

And needs to be sorted so the elements are in the following order: ~user1 ~user84 &user8 @user3 %user29 +user39 user002 user2838

After users are sorted by access level, they also need to be sorted alphabetically.

Asking here is a last resort, if someone could help me out, I'd very much appreciate it. Thankyou in advance.

A: 

How are you storing the list of user names? If it is a database then could you use SQL to retrieve the data in the order you want before you put it in a list?

BarneyHDog
+1  A: 

As long as the array contains an object then implement IComparable on the object and then call Array.Sort().

Tho if the collection is changable I would recommend using a List<>.

Stevo3000
Probably best to use IComparable<T> rather than the non-generic IComparable, or inherit off Comparable<T> which implements both
thecoop
Yes, IComparable<T> would be good if the OP was using a List<>, but I don't think that this works for Array.Sort() which requires IComparable.
Stevo3000
+1  A: 

You can use SortedList<K,V> with the K (key) implementing IComparable interface which then defines the criteria of your sort. The V can simply be null or the same K object.

Igor Brejc
He'd be wanting to sort on the values though, not the keys... seems a very, very roundabout way of doing it.
Matthew Scharley
Not really - keys == values in this case. And if you already have a sorted list, calling Sort() each time you add an element is pretty inefficient. Anyway, if you want a cleaner solution, you can use PowerCollections' OrderedSet<T>, for example (http://www.codeplex.com/PowerCollections)
Igor Brejc
+1  A: 

You can give an IComparer<T> or a Comparison<T> to Array.Sort. Then you just need to implement the comparison yourself. If it's a relatively complex comparison (which it sounds like this is) I'd implement IComparer<T> in a separate class, which you can easily unit test. Then call:

Array.Sort(userNames, new UserNameComparer());

You might want to have a convenient instance defined, if UserNameComparer has no state:

Array.Sort(userNames, UserNameComparer.Instance);

List<T> has similar options for sorting - I'd personally use a list rather than an array, if you're going to be adding/removing items regularly.

In fact, it sounds like you don't often need to actually do a full sort. Removing a user doesn't change the sort order, and inserting only means inserting at the right point. In other words, you need:

  • Create list and sort it to start with
  • Removing a user is just a straightforward operation
  • Adding a user requires finding out where to insert them

You can do the last step using Array.BinarySearch or List.BinarySearch, which again allow you to specify a custom IComparer<T>. Once you know where to insert the user, you can do that relatively cheaply (compared with sorting the whole collection again).

Jon Skeet
A: 

You should take a look at the IComparer interface (or it's generic version). When implementing the CompareTo method, check whether either of the two usernames contains one of your reserved character. If neither has a special reserved character or both have the same character, call the String.CompareTo method, which will handle the alphabetical sorting. Otherwise use your custom sorting logic.

Hirvox
A: 

I gave the sorting a shot and came up with the following sorting approach:

List<char> levelChars = new List<char>();
levelChars.AddRange("+%@&~".ToCharArray());
List<string> names = new List<string>();
names.AddRange(new[]{"~user1", "~user84",  "@user3", "&user8", "+user39", "user002", "user2838", "%user29"});
names.Sort((x,y) =>
               {
                   int xLevel = levelChars.IndexOf(x[0]);
                   int yLevel = levelChars.IndexOf(y[0]);

                   if (xLevel != yLevel)
                   {
                       // if xLevel is higher; x should come before y
                       return xLevel > yLevel ? -1 : 1;
                   }

                   // x and y have the same level; regular string comparison
                   // will do the job
                   return x.CompareTo(y);                       
               });

This comparison code can just as well live inside the Compare method of an IComparer<T> implementation.

Fredrik Mörk