1

What is a good solution to keep an ObservableCollection that is bound to a ListView sorted alphabetically? It seems Windows 8.x does not offer sorting on the CollectionViewSource, so I will need to sort the ObservableCollection.

The collection needs to be in the proper sort order every time a string is added to it, so I believe what I need is a method to insert the data into the proper spot rather than adding it to the ObservableCollection and then sorting that collection, but I haven't found a good method to do this yet. Would appreciate any suggestions on this.

Rexfelis
  • 57
  • 3
  • 10

2 Answers2

1

One way to insert without affecting the whole collection is to first find the index the new item will be inserted at, and then just use the built-in Insert to add it to the collection.

An extension method would be perfect in this case.

public static void InsertInOrder<T>(this IList<T> list, T newItem, IComparer<T> comparer = null)
{
    comparer = comparer ?? Comparer<T>.Default;

    var index = Array.BinarySearch<T>(list.ToArray<T>(), newItem, comparer);

    if (index >= 0)
    {
        throw new ArgumentException("Cannot insert duplicated items");
    }
    else
    {
        list.Insert(~index, newItem);
    }
}

Assure you have a collection like this,

ObservableCollection<string> _collection = new ObservableCollection<string> { "a", "b", "c", "e", "f" };

Then to call the extension method, you do

_collection.InsertInOrder("d");

Hope this helps!

Justin XL
  • 38,763
  • 7
  • 88
  • 133
0

ObservableCollection<T> implements IList<T>, so you could just do a binary search on it as a list every time you need to add something, and insert at the appropriate location:

public static class ListHelper
{
    public static int BinarySearchFirst<T>(this IList<T> list, T item, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;
        int start = list.BinarySearch(item, comparer);
        for (; start > 0 && comparer.Compare(list[start], list[start - 1]) == 0; start--)
            ;
        return start;
    }

    public static int BinarySearchLast<T>(this IList<T> list, T item, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;
        int start = list.BinarySearch(item, comparer);
        if (start > 0)
        {
            for (int end = list.Count - 1; start < end && comparer.Compare(list[start], list[start + 1]) == 0; start++)
                ;
        }
        return start;
    }

    public static int BinarySearch<T>(this IList<T> list, T value)
    {
        return BinarySearch(list, value, null);
    }

    // Searches the list for a given element using a binary search
    // algorithm. Elements of the list are compared to the search value using
    // the given IComparer interface. If comparer is null, elements of
    // the list are compared to the search value using the IComparable
    // interface, which in that case must be implemented by all elements of the
    // list and the given search value. This method assumes that the given
    // section of the list is already sorted; if this is not the case, the
    // result will be incorrect.
    //
    // The method returns the index of the given value in the list. If the
    // list does not contain the given value, the method returns a negative
    // integer. The bitwise complement operator (~) can be applied to a
    // negative result to produce the index of the first element (if any) that
    // is larger than the given search value. This is also the index at which
    // the search value should be inserted into the list in order for the list
    // to remain sorted.
    public static int BinarySearch<T>(this IList<T> list, T value, IComparer<T> comparer)
    {
        // Adapted from http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs
        if (list == null)
            throw new ArgumentNullException("list");
        comparer = comparer ?? Comparer<T>.Default;
        int lo = 0;
        int hi = list.Count - 1;
        while (lo <= hi)
        {
            int i = lo + ((hi - lo) >> 1);
            int order = comparer.Compare(list[i], value);

            if (order == 0)
                return i;
            if (order < 0)
            {
                lo = i + 1;
            }
            else
            {
                hi = i - 1;
            }
        }

        return ~lo;
    }

    public static int AddToSortedList<T>(this IList<T> list, T value, bool allowDuplicates)
    {
        return list.AddToSortedList(value, allowDuplicates, null);
    }

    public static int AddToSortedList<T>(this IList<T> list, T value, bool allowDuplicates, IComparer<T> comparer)
    {
        // If the incoming value is equivalent to the some value already in the list using the current comparer,
        // add it to the END of the sequence of equivalent entries.
        int index = list.BinarySearchLast(value, comparer);
        if (!allowDuplicates && index >= 0)
            return index;
        if (index < 0)
            index = ~index;
        list.Insert(index, value);
        return index;
    }
}

It's debatable whether AddToSortedList() should be an extension method or a just some static helper method, since many lists will not be sorted. In case of trying to add an entry to the list that is equivalent to some pre-existing entries, my method adds the new entry to the end of the sequence of equivalent entries, or returns the index of the existing entry, as specified by the caller. You might simply prefer to throw an exception, depending upon your needs.

dbc
  • 104,963
  • 20
  • 228
  • 340