7

I need the ability to sort a collection of customers which contains a property of numeric string.

How Can I sort the below collection by code in numeric order. Again Code is a string.

           class Program
           {
              static void Main(string[] args)
              {
                 SortableObservableCollection<Customer> customerList = new SortableObservableCollection<Customer>();
                 customerList.Add(new Customer() {Name = "Jo", Code = "1"});
                 customerList.Add(new Customer() { Name = "Jo", Code = "10" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "11" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "9" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "7" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "12" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "13" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "2" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "5" });
                 customerList.Add(new Customer() { Name = "Jo", Code = "7" });

                 //Order them by Code How
              }
           }

           public class Customer
           {
              public string Name { get; set; }
              public string Code { get; set; }
           }

Thanks for any suggestions

2

4 Answers 4

20

Option 1: implement IComparer<Customer> and parse the Code within that

Option 2: use LINQ to do the same thing:

customerList = customerList.OrderBy(c => int.Parse(c.Code)).ToList();

Option 3: change the Customer class so that a numeric value is stored as a numeric type :)

EDIT: As noted, this will throw an exception if you give it a customer with a non-numeric code.

Also, I'm calling ToList() because I assumed you still needed it to be a list. If you just need to iterate over the results in order, then use:

IEnumerable<Customer> ordered = customerList.OrderBy(c => int.Parse(c.Code));

Note that if you iterate over that twice, however, it will perform all the parsing and sorting twice too.

Sign up to request clarification or add additional context in comments.

3 Comments

It is only work if code property is successfully parsed into numerical.
Excellent all works with int.Parse. Will accept once site will let me.6 mins
@saurabh: Yes, if there's invalid data it will blow up. I wouldn't put data validation at this step though... I'd put it much earlier. Of course, if it's valid to have non-numeric strings for a Customer then it wouldn't work... but then I would hope the question would have been worded differently.
7

If the keys are always just numbers and those numbers are always convertible to ints then it is pretty straightforward. Just convert them to ints.

var sorted = from customer in customerList orderby Int32.ParseInt(customer.Code) select customer;

If any of them are not ints then this will crash.

Another way to do it is to pad the string out with leading zeros:

var sorted = from customer in customerList orderby PadToTenDigits(customer.Code) select customer;

where PadToTenDigits is a method left as an exercise, that turns "1" into "0000000001" and "1000" into "0000001000", and so on.

If you have to do true "numeric" sorting on complex codes, where, say, code "A300-B" sorts before "A1000-XYZ", then you have a harder problem on your hands. The trick there is to break up the code into "parts" and do a stable sort on each part. Basically, a radix sort.

7 Comments

I like the PadToTenDigits approach. It seems to perform a bit faster (about 3.5x on my build) than my twice-parsing integer method. It's also a simple enough delegate/function to write.
Of course, the benefit to the way I did it is that (a) it only compares numbers, and not potentially '000000ABCD', and you get a little control over if non-numeric/empty input gets sorted to the front (int.MinValue) or back (int.MaxValue) of the line. Then again, PadToTen could potentially be coded to meet these same objectives.
@Anthony: It's possible to rewrite your approach to use only one call to int.TryParse - it just means a somewhat uglier lambda expression (a full block lambda, which thus wouldn't work in a query expression). That also wouldn't generate as much garbage, I suspect. On the other hand, I do still like the pad to ten digits approach :)
(Of course, neither of these approaches is really as fast as it might be. You don't need to actually create a padded string to effectively "pretend" you've got one. Likewise if you've got two strings of different lengths and the longer one starts with a non-zero digit, you know you're done. This could be micro-optimized to heck and back... but I'd definitely wait until I'd shown this to be an issue before worrying about it.)
(Still rambling...) One interesting point about using OrderBy is that the projection to a key will only be performed once per item. Compare that with using List<T>.Sort with a custom IComparer<T>... unless you have some very elaborate scheme, you're going to end up parsing the code on each comparison. There's nowhere obvious to cache the result (assuming you can't modify Customer).
|
7

How confident are you that the Code is (a) numeric and (b) present?

var sortedList = customerList.OrderBy(c => int.Parse(c.Code));

If you have doubts, try another approach

Func<string, bool> isInteger = s => { int temp; return int.TryParse(s, out temp); };
var query = customerList.OrderBy(c => isInteger(c.Code) ? int.Parse(c.Code) : int.MaxValue);

Or to reduce the number of attemtps to parse the value and to keep the logic in one delegate:

Func<string, int> intParser = input =>
{
    int result;
    if (!int.TryParse(input, out result))
        return int.MaxValue; 

    return result;
};

var query = customerList.OrderBy(c => intParser(c.Code)); // .ToList() if you wish

Note: I'm using int.MaxValue to move non-integer inputs to the back of the line. You can choose another value depending upon where you might want those objects.

1 Comment

this one is much acceptable. +1 for taking note of alphabet and numeric string
-2

Try LINQ to Objects:

var result = from c in customerList
             orderby Int.Parse(c.Code)
             select c;

3 Comments

That will sort "100" before "2".
That would work, but I believe that since its a String, 10 would sort above 1
Oversight. Thank you both, @Jon Skeet and @Nate Bross. I corrected my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.