How to sort alphanumeric values with jQuery and C#
Sorting values on the server side is usually pretty trivial. There are a lot of tools at your disposal, not the least of which is the data query itself. When it comes to sorting an element on the front end however, it can sometimes take a little bit of clever data value usage to accomplish your goal, especially if you don't want to make another call to the server.
For this example, let's assume that we have a list of visitors staying at a hotel. Each visitor has a visit with basic data describing that visit, such as their name, room number, length of stay, etc. Imagine that you are laying out each of those current visits on a web application dashboard for a quick view of who is staying at the hotel at the moment. Each visit might be represented by a 200 x 200 block of data that we might call a 'card'. Initially these cards might be sorted by Last Name but say you want to change that sort order so that it is ordered by Room Number instead. If your room numbers are plain integers this is pretty easy, but what if they consist of letters,numbers, and other special characters? For example, 407 W to represent the west wing of the building.
When I need to do some element sorting using jQuery I turn to TinySort, the fantastic plugin by Ron Valstar. It is very flexible, well documented, and at just 1kB gzipped, tiny. Out of the box, TinySort does some very good sorting, even on mixed literal and numeric strings. For example (from the TinySort docs):
mixed literal and numeral
In a normal sort the order would be a1,a11,a2 while you'd really want it to be a1,a2,a11. TinySort corrects this:
$(".visit-list ul li").tsort();
- eed140ler111
- eil26viu117
- omi111jea23
- uin115oem6
- uin1oem121
- uin37oem109
- uin84oem57
- uin84oem78
- uin84oem88
In my use however, I was unable to get the proper sort order when numbers were followed by a blank space, then a letter, i.e "407 W". To correct this, and to gain more control of how the objects would be sorted, I decided to use TinySort's ability to specify what attribute I'd like to sort on. I created a custom HTML5 data element called data-room and told TinySort to use the value of that attribute when sorting:
$(".visit-list ul li").tsort({ attr: 'data-room'});
Then in my HTML I use that data element on the objects I want to sort:
<div class="visit-list"> <ul> <li data-room="404 W"> Visit Details </li> </ul> </div>
The next step is to control the value of that data-room attribute more precisely. In this example I'm using C#.NET MVC 4 with the Razor view engine on the server side. I want to convert the room number into a more basic form, in my case I decided to turn everything into a decimal for easy sorting. To do this, I created an extension method so that I'd have access to it conveniently throughout my application. The extension method looks like this:
public static decimal ToSortableDecimal(this string str) { if (String.IsNullOrWhiteSpace(str)) return 0; char[] alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); StringBuilder sb = new StringBuilder(); foreach (var c in str.ToUpper().ToCharArray()) { int result; if (int.TryParse(c.ToString(), out result)) { sb.Append(c); } else { if (c == ' ' || Array.IndexOf(alpha, c) == -1) { //ignore } else { if (!sb.ToString().Contains('.')) sb.Append("."); sb.Append(Array.IndexOf(alpha, c)); } } } return decimal.Parse(sb.ToString()); }
When you pass a string through this function, several things happen:
- All non alphanumeric characters are eliminated
- If a numeric character is encountered, it is added to the decimal at the current position
- If a letter is encountered, a decimal is added if one does not yet exist
- If a letter is encountered, it's position in the alphabet is determined and that index position is used in place of the letter
For example, "0965 D" becomes "965.3" which is much easier to sort and a lot more clear in terms of how the sort will behave.
Now you just need to use this room number conversion in the HTML:
<div class="visit-list"> <ul> @foreach (var visit in Model.Visits) { <li data-room="@visit.RoomNumber.ToSortableDecimal()"> Visit Details </li> } </ul> </div>
When you trigger TinySort on those elements, you will get a nice, tidy decimal sort.
Meet the Author
Matthew Mombrea
Matt is our Chief Technology Officer and one of the founders of our agency. He started Cypress North in 2010 with Greg Finn, and now leads our Buffalo office. As the head of our development team, Matt oversees all of our technical strategy and software and systems design efforts.
With more than 19 years of software engineering experience, Matt has the knowledge and expertise to help our clients find solutions that will solve their problems and help them reach their goals. He is dedicated to doing things the right way and finding the right custom solution for each client, all while accounting for long-term maintainability and technical debt.
Matt is a Buffalo native and graduated from St. Bonaventure University, where he studied computer science.
When he’s not at work, Matt enjoys spending time with his kids and his dog. He also likes to golf, snowboard, and roast coffee.
0 Comments