none
KeyedCollection - How To Sort???

    Question

  • I have a custom collection created that derives from KeyedCollection.  Everything works great exept I want to be able to sort based on a property in one of the objects in the collection.  How does that work as I can't seem to find any examples on sorting a KeyedCollection class?

    Below is my sample code

        public abstract class BusinessKeyedCollectionBase<K,T> : KeyedCollection<K,T> where T : IBusinessKeyedObject<K>
        {
            // This parameterless constructor delegates to the base class
            // constructor that specifies a dictionary threshold. A
            // threshold of 0 means the internal Dictionary is created
            // the first time an object is added.

            public BusinessKeyedCollectionBase()
                : base(null, 0)
            { }

            protected override K GetKeyForItem(T t)
            {
                return t.Key;
            }

            public void Sort(string sortExpression)
            {
               
            }
        }

        public interface IBusinessKeyedObject<K>
        {
            // Property signatures:
            K Key
            {
                get;
                set;
            }

        }

     

    Thursday, February 22, 2007 7:18 PM

Answers

  • You can sort the underlying collection by accessing the Items property.  Cast the IList interface to a concrete List<T> and you can access the Sort() method.

    You will need to define a Comparer in order to implement sorting by property.  I have worked up a quick example that you can improve in.

        class BusinessKeyedCollection<K,T> : KeyedCollection<K,T> where T:IBusinessObject<K>
        {
            protected override int GetKeyForItem(T item)
            {
                return item.Key;
            }

            public void Sort(string property)
            {
                List<T> list = base.Items as List<T>;
                if (list != null)
                {               
                    if (String.IsNullOrEmpty(property))
                    {
                        property = "Key";
                    }
                    ObjectComparer<T> comparer = new ObjectComparer<T>(property);
                    list.Sort(comparer);
                }
            }

        }

        class ObjectComparer<T> : Comparer<T>
        {
            PropertyInfo m_propertyInfo;

            public ObjectComparer(PropertyInfo pi)
            {
                m_propertyInfo = pi;
            }

            public ObjectComparer(string property)
            {
                m_propertyInfo = typeof(T).GetProperty(property);
            }

            public override int Compare(T x, T y)
            {
                Debug.Assert(m_propertyInfo != null);

                if (x == null && y==null)
                {
                    return 0;
                }
                else if (x == null)
                {
                    return -1;
                }

                IComparable xComparer = m_propertyInfo.GetValue(x, null) as IComparable;
                object yValue = m_propertyInfo.GetValue(y, null);

                if (xComparer == null)
                {
                    throw new ArgumentOutOfRangeException("Object does not support IComparable interface");
                }

                return xComparer.CompareTo(yValue);

            }
        }
    • Marked as answer by M_Over Wednesday, August 20, 2008 5:18 PM
    Thursday, February 22, 2007 11:57 PM

All replies

  • You can sort the underlying collection by accessing the Items property.  Cast the IList interface to a concrete List<T> and you can access the Sort() method.

    You will need to define a Comparer in order to implement sorting by property.  I have worked up a quick example that you can improve in.

        class BusinessKeyedCollection<K,T> : KeyedCollection<K,T> where T:IBusinessObject<K>
        {
            protected override int GetKeyForItem(T item)
            {
                return item.Key;
            }

            public void Sort(string property)
            {
                List<T> list = base.Items as List<T>;
                if (list != null)
                {               
                    if (String.IsNullOrEmpty(property))
                    {
                        property = "Key";
                    }
                    ObjectComparer<T> comparer = new ObjectComparer<T>(property);
                    list.Sort(comparer);
                }
            }

        }

        class ObjectComparer<T> : Comparer<T>
        {
            PropertyInfo m_propertyInfo;

            public ObjectComparer(PropertyInfo pi)
            {
                m_propertyInfo = pi;
            }

            public ObjectComparer(string property)
            {
                m_propertyInfo = typeof(T).GetProperty(property);
            }

            public override int Compare(T x, T y)
            {
                Debug.Assert(m_propertyInfo != null);

                if (x == null && y==null)
                {
                    return 0;
                }
                else if (x == null)
                {
                    return -1;
                }

                IComparable xComparer = m_propertyInfo.GetValue(x, null) as IComparable;
                object yValue = m_propertyInfo.GetValue(y, null);

                if (xComparer == null)
                {
                    throw new ArgumentOutOfRangeException("Object does not support IComparable interface");
                }

                return xComparer.CompareTo(yValue);

            }
        }
    • Marked as answer by M_Over Wednesday, August 20, 2008 5:18 PM
    Thursday, February 22, 2007 11:57 PM
  • Thanks for the help and the for the code.
    Friday, March 02, 2007 2:56 PM
  • I've been interested in a SortedKeyedCollection for a while, and finally got down to writing my own. I write a lot of API code for other teams in my company, so as per Microsoft guidelines, using SortedList and SortedDictionary isn't really an option; and I would rather have the collection sorted automatically, rather than having to call the Sort() method as you have. I've put up a blog entry about it, but here's my code in brief:

    using System.Collections.Generic; 
    using System.Collections.ObjectModel; 
     
    public abstract class SortedKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem> 
        protected virtual IComparer<TKey> KeyComparer 
        { 
            get 
            { 
                return Comparer<TKey>.Default; 
            } 
        } 
     
        protected override void InsertItem(int index, TItem item) 
        { 
            int insertIndex = index; 
     
            for (int i = 0; i < Count; i++) 
            { 
                TItem retrievedItem = this[i]; 
                if (KeyComparer.Compare(GetKeyForItem(item), GetKeyForItem(retrievedItem)) < 0) 
                { 
                    insertIndex = i; 
                    break
                } 
            } 
     
            base.InsertItem(insertIndex, item); 
        } 
    }  
     

    It can be easily extended, so if you have some weird key, you just need to provide your own comparer to handle the sorting.
    • Edited by David Keaveny Wednesday, June 25, 2008 11:51 PM Fixed typo
    Wednesday, June 25, 2008 11:48 PM
  • Old post, I know.  Just wanted to provide input as this is probably helpful for many of us.

    The code presented by David Keaveny can be greatly improved by actually using a QuickSort algorithm in InsertItem().  As it is shown here, the operation is O(N) which is awful on large collections.


    Jose R. MCP
    Code Samples

    Monday, July 22, 2013 9:12 PM