Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

C# Sorting Custom Class

Posted on 2013-01-17
5
Medium Priority
?
435 Views
Last Modified: 2013-01-22
Hi,

I have a class called "TradeHolder" (code below). I have a List<TradeHolder> which I wish to sort. The question I have is sometimes I want sort the List on "MaxWin" (double) other times I will need to sort it on "TradeReturn" (double) other times "PeriodLosing" (int) etc.

Also I may need to sort it ascending or descending. How is the best way to do this using the IComparable interface?

public class TradeHolder : IComparable<TradeHolder>
            {
                public double TradeReturn, BasePnL, MaxLose, MaxWin;
                public int PeriodInTrade, PeriodLosing;
                public string ExitReason, LockInLevel;
                public Boolean LockInActive;
                public enumOutCome Outcome;

                public int IComparable.CompareTo(TradeHolder TH)
                {
                    
                }

            }

Open in new window


Thanks,
M
0
Comment
Question by:mcs26
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
5 Comments
 
LVL 75

Accepted Solution

by:
käµfm³d   👽 earned 2000 total points
ID: 38789212
I would suggest creating a new class which implements IComparer rather than implementing the IComparable interface on your existing class. With the former, it easy to create new comparers which sort on the property you specify. For example:

public MaxWinComparer : IComparer<TradeHolder>
{
    private bool _sortDescending;
    
    public MaxWinComparer(bool sortDescending)
    {
        this._sortDescending = sortDescending;
    }
    
    public MaxWinComparer() : this(false)  // sort ascending by default
    {
    
    }
    
    public int Compare(TradeHolder x, TradeHolder y)
    {
        if (this._sortDescending)
        {
            return y.MaxWin.CompareTo(x.MaxWin);
        }
        else
        {
            return x.MaxWin.CompareTo(y.MaxWin);
        }
    }
}

Open in new window


Now when you need to sort on a different field (like TradeReturn or PeriodLosing), you can create a class for that field, and just switch the property that is being compared.

When it comes to performing the actual sort, just pass a new instance of one of your comparers to the the List class' Sort method:

List<TradeHolder> someList = // initialized somewher

someList.Sort(new MaxWinComparer());  // ascending
someList.Sort(new MaxWinComparer(true));  // descending

Open in new window

0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 38789228
P.S.

Some fun I had recently was implementing a comparer that would be a bit more flexible in not requiring me to have create new classes for every property I wanted to compare. It's a bit more verbose to use, but if you are using .NET 3.0+, then you can do something like:

using System;
using System.Collections.Generic;

namespace Transcripts.Core.Business.Utilities.Comparers
{
    public class PropertyComparer<T, TKey> : IComparer<T>
    {
        Func<T, TKey> propSelector;
        bool sortAscending;

        public PropertyComparer(Func<T, TKey> keySelector)
            : this(keySelector, false)
        {
        }

        public PropertyComparer(Func<T, TKey> keySelector, bool sortDescending)
        {
            this.propSelector = keySelector;
            this.sortAscending = !sortDescending;
        }

        public int Compare(T x, T y)
        {
            int result;
            TKey left;
            TKey right;

            if (this.sortAscending)
            {
                left = this.propSelector(x);
                right = this.propSelector(y);
            }
            else
            {
                left = this.propSelector(y);
                right = this.propSelector(x);
            }

            result = Comparer<TKey>.Default.Compare(left, right);  // Allows for Nullable types to be used as the key. If you only have types which implement IComarable (e.g. int, double, string, etc.), then you can change this to "left.CompareTo(right)" for a bit better efficiency

            return result;
        }

        public bool SortsAscending { get { return this.sortAscending; } }
    }
}

Open in new window


...and usage would look like:

someList.Sort(new PropertyComparer<TradeHolder, double>(i => i.MaxWin));
someList.Sort(new PropertyComparer<TradeHolder, double>(i => i.TradeReturn));

Open in new window


...where the first type parameter to the PropertyComparer is the type of items in the list (TradeHolder), and the second type parameter is the type of thing being compared (double)--MaxWin and TradeReturn in my examples above.
0
 

Author Comment

by:mcs26
ID: 38796494
Hi Kaufmed,

Thanks for the posts & code. I now understand why you suggest using the IComparer interface.

I'm being quite dumb but I do have a question. As we'r using the IComparer interface we have to implement the Compare method which is fine. However in the Compare method you use the CompareTo method. So does this mean my TradeHolder class has to implement the IComparable interface and then write some code in the CompareTo method?

Thanks,
M

    public int Compare(TradeHolder x, TradeHolder y)
    {
        if (this._sortDescending)
        {
            return y.MaxWin.CompareTo(x.MaxWin);
        }
        else
        {
            return x.MaxWin.CompareTo(y.MaxWin);
        }
    }

Open in new window

0
 
LVL 75

Expert Comment

by:käµfm³d 👽
ID: 38797716
It does not. The only reason I used CompareTo was that it functions in exactly the way I needed it to for the comparisons. The Compare method returns an integer. This integer can be the following:

-1:  the left item is less than the right item
0:   both items are equal
1: the left item is greater than the right item

Quite conveniently, this is exactly what CompareTo returns as well. Since items which implement the IComparable interface are guaranteed to have the CompareTo method, and since that method returns exactly the same information that I needed for the Compare method, I just used the CompareTo method.

Now, this is not to imply that it is required. I certainly could have implemented my own logic--so long as it returns the information above. For example:

public int Compare(TradeHolder x, TradeHolder y)
{
    if (this._sortDescending)
    {
        if (x.MaxWin < y.MaxWin)
        {
            return 1;
        }
        else if (x.MaxWin > y.MaxWin)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        if (x.MaxWin < y.MaxWin)
        {
            return -1;
        }
        else if (x.MaxWin > y.MaxWin)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

Open in new window


Here I've used no CompareTo. I just made sure to return each of the values -1, 1, & 0 accordingly. Your logic can be whatever you need it to be. Just ensure that whichever object you want to be less than the other returns -1, whichever you want to be higher than returns 1, and if they are equal return 0.

Here's an example of sorting strings where ones that contain the word "Important" (case-sensitive) go at the beginning--regardless of spelling--and the rest get sorted alphabetically:

using System;
using System.Collections.Generic;

namespace _27999436
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> someStrings = new List<string>()
            {
                "this isn't very important",
                "Important - this is important",
                "this is just another string",
                "a string that is not important",
                "Important - make sure this is first",
            };

            someStrings.Sort(new ImportantComparer());

            someStrings.ForEach(Console.WriteLine);
        }
    }

    public class ImportantComparer : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            bool xIsImportant = x.Contains("Important");
            bool yIsImportant = y.Contains("Important");

            if (xIsImportant)
            {
                if (yIsImportant)
                {
                    return x.CompareTo(y);
                }
                else
                {
                    return -1;
                }
            }
            else if (yIsImportant)
            {
                return 1;
            }
            else
            {
                return x.CompareTo(y);
            }
        }
    }
}

Open in new window


...and to make it so Important strings are at the beginning, and everything else is sorted descending:

...
            else
            {
                return y.CompareTo(x);  // flip x and y
            }
        }
    }
}

Open in new window

0
 

Author Closing Comment

by:mcs26
ID: 38807355
Thanks again for the clear explanation
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

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:…
This article aims to explain the working of CircularLogArchiver. This tool was designed to solve the buildup of log file in cases where systems do not support circular logging or where circular logging is not enabled
This course is ideal for IT System Administrators working with VMware vSphere and its associated products in their company infrastructure. This course teaches you how to install and maintain this virtualization technology to store data, prevent vuln…
In this video, Percona Solutions Engineer Barrett Chambers discusses some of the basic syntax differences between MySQL and MongoDB. To learn more check out our webinar on MongoDB administration for MySQL DBA: https://www.percona.com/resources/we…

715 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