2

Does .NET have an in place sort function for IList? I need to sort the collection itself, not create a new collection.

EDIT: The reason I need to do the sort in place is to avoid redrawing the UI unless something has actually changed. I don't want to create a hundred new custom controls if only one item is out of place.

EDIT: Again, this is a question about in place sorting, not just sorting in general.

Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
  • Pretty sure that's a no. I think `Array` can, but is it really so critical that you can't generate a new collection? – sircodesalot Oct 03 '13 at 19:07
  • 1
    No, it's not part of the interface. Easy to do though with a little LINQ. – LarsTech Oct 03 '13 at 19:07
  • Doesn't LINQ still create a new collection? – sircodesalot Oct 03 '13 at 19:08
  • Possible duplicate of [Sorting an IList in C#](http://stackoverflow.com/questions/15486/sorting-an-ilist-in-c-sharp) – dana Aug 19 '16 at 04:33
  • When you sort a collection, the collection is greatly modified, but the items in the list are not touched. When you call `IEnumerable.OrderBy().ToList()` you get a new, modified (aka sorted) list, but the items in the list are unchanged. There's not a whole lot of difference. What's your fear? – Flydog57 Oct 08 '21 at 02:46

3 Answers3

3

How about an in-place Quicksort / InsertionSort / ShellSort implementation:

public static class InPlaceQuickSort {
    private static void Swap<T>(IList<T> set, int left, int right) {
        T temp = set[left];
        set[left] = set[right];
        set[right] = temp;
    }

    private static Int32 Partition<T>(IList<T> set, int lBound, int rBound)
        where T : IComparable<T> {
        T pivot = set[rBound];
        int left = lBound - 1;
        int right = rBound;

        while (true) {

            while (set[++left].CompareTo(pivot) < 0) ;
            while (set[--right].CompareTo(pivot) > 0) if (left == right) break;

            if (left >= right) break;
            Swap(set, left, right);
        }

        Swap(set, left, rBound);
        return left;
    }

    private static IList<T> QuickSort<T>(IList<T> set, int lBound, int rBound)
        where T : IComparable<T> {
        if (lBound >= rBound) return set;

        Int32 pivot = Partition(set, lBound, rBound);
        QuickSort(set, lBound, pivot - 1);
        QuickSort(set, pivot + 1, rBound);

        return set;
    }

    public static IList<T> InsertionSort<T>(this IList<T> set)
        where T : IComparable<T> {
        for (Int32 index = 1; index < set.Count; index++) {
            for (Int32 insertion = index; insertion > 0 && set[insertion - 1].CompareTo(set[insertion]) > 0; insertion--) {
                Swap(set, insertion - 1, insertion);
            }
        }

        return set;
    }

    public static IList<T> ShellSort<T>(this IList<T> set)
        where T : IComparable<T> {
        Int32 shell = 1;

        while (shell < (set.Count / 3)) shell = shell * 3 + 1;

        while (shell >= 1) {
            for (Int32 index = shell; index < set.Count; index++) {
                for (Int32 insertion = index; insertion >= shell && set[insertion - shell].CompareTo(set[insertion]) > 0; insertion -= shell) {
                    Swap(set, insertion - shell, insertion);
                }
            }

            shell = shell / 3;
        }

        return set;
    }

    public static IList<T> QuickSort<T>(this IList<T> set)
        where T : IComparable<T> {
        return QuickSort<T>(set, 0, set.Count - 1);
    }
}

And, here's how you use it:

public static void Main() {
    List<Int32> numbers = new List<int> { 1, 3, 2, 4, 2 };

    foreach (Int32 number in numbers.QuickSort())
        Console.WriteLine(number);

    Console.ReadLine();
}
sircodesalot
  • 11,231
  • 8
  • 50
  • 83
  • 2
    I don't know if returning the list is a good idea. Typically when functions return a modified list, it's assumed they don't modify the old one. – cHao Oct 03 '13 at 19:25
  • Yeah, not sure the reasoning behind choosing `IList`, but I cooked this up made to order. – sircodesalot Oct 03 '13 at 19:26
  • QuickSort is a bad choice for UI work. It's worse-case scenario, a sorted or nearly sorted list, is triggered whenever a new item is added to the list. – Jonathan Allen Oct 03 '13 at 19:27
  • @JonathanAllen EDIT: Added insertion sort. – sircodesalot Oct 03 '13 at 19:29
  • I forgot about that one. Insertion sort sounds like the perfect solution for my design. – Jonathan Allen Oct 03 '13 at 19:38
  • 1
    Nice solution, but I'd suggest avoiding complexity by utilizing .NET's built-in sorting algorithms, which also have the benefit of being optimized and tested for over a decade. – Moho Oct 03 '13 at 19:39
  • 1
    @JonathanAllen also added shell sort since that may be faster than regular insertion sort. – sircodesalot Oct 03 '13 at 19:41
  • 2
    @Moho: Wasn't the point of the question that .NET does not provide "an in place sort function for IList", so built-in algorithms wouldn't do? – O. R. Mapper Oct 03 '13 at 19:42
  • Yup - but would you rather maintain a simple call to `list.OrderBy( i => i )` or the above code? Which has been tested more and presumably optimized depending on the type to be sorted? – Moho Oct 03 '13 at 19:46
  • CS *is* algorithms. Computer scientists should know how to maintain classical algorithms. There isn't really much point to getting a formal CS education if you're then going to turn around and use prefabricated solutions. – sircodesalot Oct 04 '13 at 15:09
  • I don't understand why you made the public method return the sorted collection and put the call to quicksort inside the foreach; that's a pattern one would expect from a sort method that returns a new sorted collection rather than in-placing the old. I appreciate that it doesn't make a difference in this narrow example but if there were more than one loop you'd inplace once at the start of them all.. in essence I think I'd have made it `void` like Array.Sort is.. – Caius Jard Oct 08 '21 at 05:58
1

Another take on the extension method solution by keeping it simple and using .NET's built-in sorting (also allowing for custom IComparers

public static class IListExtensions
{
    public static void Sort<T>(this IList<T> list)
    {
        var orderedList = list.OrderBy(i => i).ToArray();

        for( int i = 0; i < list.Count; ++i )
        {
            list[i] = orderedList[i];
        }
    }

    public static void Sort<T>(this IList<T> list, IComparer<T> comparer )
    {
        var orderedList = list.OrderBy(i => i, comparer).ToArray();

        for (int i = 0; i < list.Count; ++i)
        {
            list[i] = orderedList[i];
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var orig = new List<int>() { 2, 3, 1 };
        var toOrder = orig;

        Console.Write("Orig: ");
        orig.ForEach(i => Console.Write("{0} ", i));
        toOrder.Sort();
        Console.Write("\nOrdered: ");
        toOrder.ForEach(i => Console.Write("{0} ", i));
        Console.Write("\nOrig: ");
        orig.ForEach(i => Console.Write("{0} ", i));


        Console.ReadLine();
    }

}
Moho
  • 15,457
  • 1
  • 30
  • 31
0

No. Use IEnumerable.OrderBy() or SortedList<T>, both of which will create new collections. Question is why can't you create a new collection?

Moho
  • 15,457
  • 1
  • 30
  • 31
  • It is UI-bound, so creating a new collection would require destroying and rebuilding a large part of the screen. – Jonathan Allen Oct 03 '13 at 19:16
  • Also, if the list is already in order then the operation should be a no-op. – Jonathan Allen Oct 03 '13 at 19:21
  • How would the list know it is ordered? – Moho Oct 03 '13 at 19:22
  • 1
    @Moho: Lists are by definition ordered. If you mean sorted, then the list will be sorted til something is added to it. Re-sort when you add, using an algorithm whose best case is an already-nearly-sorted list, and there ya go. – cHao Oct 03 '13 at 19:33
  • If you use bubble sort then it does a pass to verify that the items are in order. Though it has a bad rep, bubble sort is actually pretty damn fast when dealing with lists that are already in order or close to it. – Jonathan Allen Oct 03 '13 at 19:33
  • Yes, cHao, I meant sorted, not ordered. – Moho Oct 03 '13 at 19:37
  • Why does creating a new container for the items mean you have to tear down your UI? Maybe I'm missing something – Flydog57 Oct 08 '21 at 02:47