How to sort Lists in C# or: IComparer rocks!
For a game i’m working on i needed to sort cities based on the commodities that are sold in these cities. The list should be sortable by buy/sell price, estimated profit with current player cargo and city names.
As my commodity and city classes are quite complex i feared that i would have to write a custom search class for this. As i didn’t really wanted that, i had a look around and soon found out, that C# has numerous ways of helping the user sorting his stuff. In fact, lists already have a Sort() function implemented which uses Quick Sort.
My first implementation looked something like this:
1 2 3 4 5 6 7 8 9 |
public List<City> GetSortedCities(string commodityName) { //I don't want to change the original list, so a new one is created here List<City> _sortedCities = new List<City>( GetCities() ); //Sort the cities based on their commodity name. Commodities are stored in a dictionary with the commodity name as key. _sortedCities.Sort((x, y) => x.commodities[commodityName].priceBuy.CompareTo(y.commodities[commodityName].priceBuy)); return _sortedCities; } |
This worked great, but i wanted a more general approach which would not only let me sort cities based on commodity prices, but virtually anything. So i had a look at IComparer which is actually exactly what i needed.
Basically you just write a custom comparer for each class you want to sort, pass that to the list’s Sort() function and you are set. Great!
1 2 3 4 5 6 7 8 9 |
public List<City> GetSortedCities(IComparer<City> cityComparer) { //I don't want to change the original list, so a new one is created here List<City> _sortedCities = new List<City>(GetCities()); //Sort cities using the supplied comparer _sortedCities.Sort(cityComparer); return _sortedCities; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public class SortByNameAsc : IComparer<City> { public int Compare( City x, City y ) { return string.Compare( x.name, y.name ); } } public class SortByPriceBuyAsc : IComparer<City> { string commodityName; public SortByPriceBuyAsc(string inCommodityName) { commodityName = inCommodityName; } public int Compare( City x, City y) { if ( x.commodities[commodityName].priceBuy > y.commodities[commodityName].priceBuy ) return 1; else if ( x.commodities[commodityName].priceBuy < y.commodities[commodityName].priceBuy ) return -1; else return 0; } } public class SortByPriceBuyDesc : IComparer<City> { string commodityName; public SortByPriceBuyDesc(string inCommodityName) { commodityName = inCommodityName; } public int Compare( City x, City y) { if ( x.commodities[commodityName].priceBuy < y.commodities[commodityName].priceBuy ) return 1; else if ( x.commodities[commodityName].priceBuy > y.commodities[commodityName].priceBuy ) return -1; else return 0; } } |
And here’s how to use it:
1 2 |
List<City> citiesSortedByName = GetSortedCities( new SortByNameAsc() ); List<City> citiesSortedByGoldPrice = GetSortedCities( new SortByPriceBuyAsc("Gold") ); |
Now i can sort my cities based on their commodities, name, size, position, citizens and how many pieces of socks each citizen owns. I just have to pass a coresponding comparer to my GetSortedCities() function!
More sorting goodness can be found in this awesome stackoverflow thread.
Recent Comments