6

If I have an IOrderedEnumberable<Car>, I sort it and then do a projecting query...

is the order preserved in the projection?

For example, does this scenario work?

IOrderedEnumberable<Car> allCarsOrderedFastestToSlowest = 
            GetAllCars()
                  .OrderByDescending(car=>car.TopSpeed);

var top3FastestCarManufacturers =
            allCarsOrderedFastestToSlowest
                  .Select(car=>car.Manufacturer)
                  .Distinct()
                  .Take(3);

Does the name of the top3FastestCarManufacturers variable convey the meaning of what has really happened in the code?

Neil Fenwick
  • 6,106
  • 3
  • 31
  • 38
  • possible duplicate of [Preserving order with LINQ](http://stackoverflow.com/questions/204505/preserving-order-with-linq) – Amy B Dec 04 '10 at 22:04
  • Agree with duplicate suggestion and this question would have been answered. Almost a specific sub-question of that one. Not sure whether to delete, or vote to close with reference to the other question? – Neil Fenwick Dec 06 '10 at 17:38

2 Answers2

3

I suspect what is going to mess you up is the Distinct. This will likely reorder the results by manufacturer to produce the distinct results. I'd likely just iterate through the list until I had three distinct manufacturers.

The selection will retain the ordering but the remarks on Distinct indicate that it returns an unordered result set and that it is implementation dependent. To be sure, I wouldn't rely on it retaining the ordering and simply do it using the iteration.

var top3 = new List<string>();
foreach (var manufacturer in allCarsOrderedFastestToSlowest
                                .Select(car=>car.Manufacturer))
{
    if (!top3.Contains(manufacturer))
    {
        top3.Add(manufacturer);
        if (top3.Count == 3)
        {
            break;
        }
    }
}
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
3

The documentation for the Distinct method doesn't say anything about whether the order is preserved or not. This is probably because it depends on the underlying implementation of the source.

You can use grouping to get the desired result, by getting the fastest car from each manufacturer, and then get the three fastest from that:

var topThreeFastestCarManufacturers =
  GetAllCars()
  .GroupBy(c => c.Manufacturer)
  .Select(g => g.OrderByDescending(c => c.TopSpeed).First())
  .OrderByDescending(c => c.TopSpeed)
  .Take(3);
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • This is a good way to do it if all you need is one column (property) from the table (object), but it will break down if you need more than one -- say you wanted both the make and model of the cars from the "top 3" manufacturers. Not really a criticism of the technique, but it relies on the fact that you don't have to maintain the integrity of tuples which breaks down when you have more than one property of interest. – tvanfosson Dec 04 '10 at 15:50
  • @tvanfosson: What do you mean? It returns three car objects complete with all properties, not a single property. Ironically, your own answer *does* have this limitation... – Guffa Dec 04 '10 at 15:53
  • sorry, must not have read closely enough. I thought you were doing a query that translated into `select manufacturer, max(topspeed) from ...` I wonder what the SQL for that does look like. – tvanfosson Dec 04 '10 at 15:58