Friday, October 24, 2008

C#: How to sort list of objects by multiple columns

Problem

How to sort list of objects by multiple columns in C#

Solution

Solution is to create a comparer class for objects. Comparer class can take multiple columns as parameter. If you want to use the list of objects as a DataSource for GridView in ASP.Net page you can store the list in ViewState and sort it as many times you want without asking data again from database

Here is the Product class I want to sort by multiple columns:


    public class Product
    {
        private string code;
        private string description;
        private double price;

        public double Price
        {
            get { return price; }
            set { price = value; }
        }

        public string Code
        {
            get { return code; }
            set { code = value; }
        }

        public string Description
        {
            get { return description; }
            set { description = value; }
        }

        public Product(string CodeValue, string DescriptionValue, double PriceValue)
        {
            Price = PriceValue;
            Code = CodeValue;
            Description = DescriptionValue;
        }

    }

And here is a SortColumn class which stores sort fields and what is used with comparer class:


   public class SortColumn
    {
        public enum sortOrderEnum
        {
            Descending = 0,
            Ascending = 1
        }

        private sortOrderEnum order;
        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public sortOrderEnum Order
        {
            get { return order; }
            set { order = value; }
        }

        public SortColumn(string NameValue, sortOrderEnum OrderValue)
        {
            Name = NameValue;
            Order = OrderValue;
        }

    }

Next thing to do is write the generic object comparer class to sort one or multiple columns:


   public class GenericComparer<ComparableObject> : IComparer<ComparableObject>
    {

        private List<SortColumn> sortByColumns;

        public List<SortColumn> SortByColumns
        {
            get { return sortByColumns; }
            set { sortByColumns = value; }
        }

        public GenericComparer()
        {
        }

        public GenericComparer(List<SortColumn> SortByColumnsValue)
        {
            SortByColumns = SortByColumnsValue;
        }

        public int Compare(ComparableObject x, ComparableObject y)
        {
            if (sortByColumns != null)
            {
                // Loop through all sort fields
                foreach (SortColumn sortItem in sortByColumns)
                {
                    // Get information about property which is used as sort field
                    PropertyInfo propertyInfo = x.GetType().GetProperty(sortItem.Name);
                  
                    // Put null values to bottom of the list
                    if (propertyInfo.GetValue(x, null) == null)
                    {
                        return 1;
                    }
                    if (propertyInfo.GetValue(y, null) == null)
                    {
                        return -1;
                    }
                    

                    int compareResult = Comparer.DefaultInvariant.Compare(propertyInfo.GetValue(x, null), propertyInfo.GetValue(y, null));
                    if (compareResult != 0)
                    {
                        if (sortItem.Order == SortColumn.sortOrderEnum.Descending)
                        {
                            return -compareResult;
                        }
                        else
                        {
                            return compareResult;
                        }
                    }
                }
                return 0;
            }
            else
            {
                return 0;
            }
        }

    }

And for last thing create a console application to test how comparer class works:


    public class Program
    {
        public static void Main(string[] args)
        {

            // Create product list with data
            List<Product> products = new List<Product>();
            products.Add(new Product("123456", "Table", 1240.99));
            products.Add(new Product("000123", "Lamp", 25.10));
            products.Add(new Product("000124", "Lamp 2", 24.55));
            products.Add(new Product(null, "Sofa", 2499.00));
            products.Add(new Product("445405", "Bed", 1399.40));
            products.Add(new Product("345405", "Bed", 1499.40));

            // List of sort fields
            List<SortColumn> sortColumns = new List<SortColumn>();

            // Not sorted
            Console.WriteLine("Products in order of appearance:");
            ListProducts(products);

            // Sort by product code
            Console.WriteLine("Products ordered by Code:");
            sortColumns.Add(new SortColumn("Code", SortColumn.sortOrderEnum.Ascending));
            products.Sort(new GenericComparer<Product>(sortColumns));
            ListProducts(products);

            // Sort by description and code
            Console.WriteLine("Products ordered by Description and Code:");
            sortColumns.Clear();
            sortColumns.Add(new SortColumn("Description", SortColumn.sortOrderEnum.Ascending));
            sortColumns.Add(new SortColumn("Code", SortColumn.sortOrderEnum.Ascending));
            products.Sort(new GenericComparer<Product>(sortColumns));
            ListProducts(products);

            // Ready
            Console.ReadLine();

        }

        public static void ListProducts(List<Product> Products)
        {
            Console.WriteLine("");
            foreach (Product item in Products)
            {
                Console.WriteLine(item.Code + "\t" + item.Description + "\t" + item.Price.ToString());
            }
            Console.WriteLine("");
        }

    }

Now you are ready to test how columns are sorted...

I used following article as a base to my solution http://
69.10.233.10/KB/recipes/Sorting_with_Objects.aspx

1 comment:

  1. http://www.kettic.com/winforms_ui/multi_column_combobox.shtml

    ReplyDelete