-2

I have an ICollection<Plante> Jardin which I want to sort according to some property Plante.Taille:

private readonly ICollection<Plante> Jardin;

I use the instance method OrderByDescending :

public void TriDesc()
{
    Console.WriteLine("Tri par taille décroissante");

    IOrderedEnumerable<Plante> Temp = Jardin.OrderByDescending(a => a.Taille);
    Jardin.Clear();

    foreach (Plante p in Temp)
    {
        Console.WriteLine(p.Taille);
        Jardin.Add(p);
    }
}

What is the problem here?

I tried debugging: Temp is correctly sorted by the method but Jardin is empty at the end.

dbc
  • 104,963
  • 20
  • 228
  • 340
Gui Gui
  • 7
  • 2
  • 3
    `IOrderedEnumerable` is just a view of the list -- it doesn't copy the original data structure. When you clear the list, there's no longer anything for `IOrderedEnumerable` to point to. Just add to a new list. – Tim Roberts Jul 01 '23 at 05:33
  • Of course it is empty. _You explicitly `Clear()` it._ – Fildor Jul 01 '23 at 05:33
  • 2
    By any chance: is this meant to sort the list in-place? – Fildor Jul 01 '23 at 05:35
  • Whaaaaat, I clear the list AFTER declaring the Temp List to store my objects. – Gui Gui Jul 01 '23 at 06:55
  • 1
    As remarked in the [docs](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderbydescending), `OrderByDescending()` uses [deferred execution](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/classification-of-standard-query-operators-by-manner-of-execution#deferred). So the sorting isn't actually done until you enumerate through `Temp`, at which point `Jardin` will have been cleared. – dbc Jul 01 '23 at 07:15
  • Since it seems you want to modify `Jardin`, you should sort it in place with [`List.Sort()`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.sort?view=net-7.0). – dbc Jul 01 '23 at 07:15
  • Does that answer your question? – dbc Jul 01 '23 at 15:28

1 Answers1

1

As remarked in the docs, OrderByDescending() is implemented using deferred execution:

Deferred execution means that the operation is not performed at the point in the code where the query is declared. The operation is performed only when the query variable is enumerated, for example by using a foreach statement.

Thus the sorting isn't actually done until you enumerate through Temp, at which point Jardin will have been cleared:

// Define an ordering for `Jardin` but don't execute it yet.
IOrderedEnumerable<Plante> Temp = Jardin.OrderByDescending(a => a.Taille);

// Clear `Jardin`
Jardin.Clear();

// Execute and enumerate the ordering of `Jardin` (which is now empty):
foreach (Plante p in Temp)
{
    Jardin.Add(p);
}

Instead, you must materialize a copy of Jardin e.g. with ToList() before clearing it and re-adding items items:

// Create an ordered copy of `Jardin`
var temp = Jardin.OrderByDescending(a => a.Taille).ToList();

// Clear `Jardin`
Jardin.Clear();

// Add the ordered items back:
foreach (var p in temp)
{
    Jardin.Add(p);
}

That being said, in C# ICollection<T> is an interface that can be implemented by unordered collections such as HashSet<T> that do not necessarily preserve the order in which items are added. If you need your Jardin collection to preserve order, consider using List<T> instead:

private readonly List<Plante> Jardin;

If you do, it can be sorted in-place using List<T>.Sort():

// Sort Jardin in descending order of Taille
Jardin.Sort((x, y) => -x.Taille.CompareTo(y.Taille));

Notes:

dbc
  • 104,963
  • 20
  • 228
  • 340