2014-12-29 2 views
1

Я пытаюсь выполнить довольно простой порядок, но, похоже, борется за то, как это сделать. Возьмем, к примеру, у меня эти два класса.Linq OrderBy Sub List

public class Method 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public decimal Price { get; set; } 

    public List<Slot> Slots { get; set; } 
} 

public class Slot 
{ 
    public DateTime ExpectedDeliveryDate { get; set; } 
} 

Используя код ниже, я хочу заказать по самой дешевой цене, а затем самой быстрой датой поставки.

var methods = new List<Method>(); 

methods.Add(new Method { Id = 1, Name = "Standard", Price = 0M, Slots = new List<Slot> { new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(5).Date } } }); 
methods.Add(new Method { Id = 2, Name = "Super Fast Next Day", Price = 0M, Slots = new List<Slot> { new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(1).Date } } }); 

var b = methods.OrderBy(x => x.Price) 
    .ThenBy(y => y.Slots.OrderBy(t => t.ExpectedDeliveryDate.Date) 
     .ThenBy(t => t.ExpectedDeliveryDate.TimeOfDay)) 
      .ToList(); 

Проблема я получаю здесь в том, что я получаю сообщение об ошибке выполнения заявив, что «по крайней мере один объект должен реализовывать IComparable».

Хотя я могу исправить это, реализовав интерфейс IComparable, мне было интересно, можно ли это сделать. Я полагаю, что есть такой код (см. Ниже), он работает нормально.

var slots = new List<Slot>(); 

slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(5).Date }); 
slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(1).Date }); 
slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.AddDays(3).Date }); 
slots.Add(new Slot { ExpectedDeliveryDate = DateTime.Now.Date }); 

var d = slots.OrderBy(x => x.ExpectedDeliveryDate); 

Cheers, DS.

Извинения за присвоение имен таких переменных, как xyz в приведенном выше примере :) Код может быть скопирован и вставлен для удовольствия от манипуляции.

EDIT - Обновлен, чтобы упростить пример кода. - Ожидание результата будет после успешной сортировки

Input 
    ID  Name   Price Slot 
    1  Standard  0  DateTime.Now.AddDays(5).Date 
    2  Super Fast  0  DateTime.Now.Date 

Output 
    2  Super Fast  0  DateTime.Now.Date 
    1  Standard  0  DateTime.Now.AddDays(5).Date 

Так что мой супер быстрый вариант должен быть сверху из-за его самым дешевым и, конечно, имеет самый быстрый срок поставки.

+4

Это не ясно, что вы ожидаете, что это * значит * заказать коллекцию способов доставки их слотов. Можете ли вы предоставить образцы данных и ожидаемые результаты? И почему вы заказываете год, а затем месяц, а затем дату (что не значит, что, по-вашему, вы думаете, это значит), а не просто заказывать самим ExpectedDeliveryDate? –

+0

Извините, вы сделаете это. –

+0

@DrSchizo Часть, которую вы еще не выяснили, состоит в том, что каждый из ваших методов может иметь несколько слотов, но ваш пример ожидаемых результатов показывает только один слот для каждого метода. Каково ожидаемое поведение, когда метод имеет несколько слотов? Когда у него есть нулевые слоты? Вам нужно решить, что должно произойти в этом случае, чтобы этот вопрос имел смысл. – JLRishe

ответ

4

Вы можете использовать Enumerable.Min(), чтобы выбрать слот с самой ранней датой, например, так:

 var query = deliveryMethods 
      .OrderBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate).Year) 
      .ThenBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate).Month) 
      .ThenBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate).Date) 
      .ToList(); 

Или просто

 var query = deliveryMethods 
      .OrderBy(x => x.Slots.Min(s => s.ExpectedDeliveryDate.Date)) 
      .ToList(); 

ли быть в курсе, что Min() будет сгенерировано исключение, когда вход последовательность пуста и минимизируемый тип - тип значения. Если вы хотите, чтобы избежать исключений, вы можете сделать это:

 var query2 = deliveryMethods 
      .OrderBy(x => x.Slots.Min(s => (DateTime?)(s.ExpectedDeliveryDate.Date))) 
      .ToList(); 

Преобразуя DateTime в обнуляемом, Min() возвратит нуль для пустой последовательности, и Method объектов с пустым списком слотов упорядочиваются в начало.

+1

Я должен был потерять свои шарики, я попробовал это прошлой ночью по какой-то странной причине, и я получил исключение IComparable. Тем не менее, я думаю, что использование «Мин» является ключевым здесь :) –

1

Я хотел бы дать объяснение того, почему попытка Вас осведомленной в исходном сообщении не работает:

var xyz = deliveryMethods 
     .OrderBy(x => x.Slots.OrderBy(y => y.ExpectedDeliveryDate.Year)) 
     .ThenBy(x => x.Slots.OrderBy(y => y.ExpectedDeliveryDate.Month)) 
     .ThenBy(x => x.Slots.OrderBy(y => y.ExpectedDeliveryDate.Date)) 
     .ToList(); 

Это потому, что вы гнездились OrderBy сек внутри OrderBy с.

x.Slots.OrderBy(...) производит IEnumerable<Slot>, поэтому вы в основном говорили об этом «сравните эти IEnumerable<Slot> s друг с другом, чтобы определить порядок доставки». Но Linq не знает, как сравнить IEnumerable<Slot> с другим и решить, что происходит перед другим (IEnumerable<Slot> не реализует IComparable<T>), поэтому вы получили сообщение об ошибке.

Ответ, как указал другой пользователь, заключается в том, чтобы дать ему то, что можно сравнить с . Как вы потом уточнил, что будет самым ранним слотом для каждого способа доставки:

var xyz = deliveryMethods 
     .OrderBy(x => x.Slots.Min(y => y.ExpectedDeliveryDate)) 
     .ToList(); 

Это будет работать при условии, что каждый способ доставки имеет, по меньшей мере, один слот, но будет бросать исключение во время выполнения, если любой из них имеет нулевые интервалы (или если Slots равно null). Я дважды спросил вас, что он должен делать в этом случае, и я призываю вас разъяснить это.

Одним из возможных решений было бы включать только способы доставки, которые имеют слоты:

var xyz = deliveryMethods 
     .Where(x => x.Slots != null && x.Slots.Any()) 
     .OrderBy(x => x.Slots.Min(y => y.ExpectedDeliveryDate)) 
     .ToList(); 
+0

Хорошее объяснение по вложенности orderby. Побочным эффектом, который вы указали слотами, не содержащими каких-либо элементов или являющимися нулевыми, является проблема, но есть решение. –

+0

@DrSchizo Я предоставил одно возможное решение проблемы без слотов/нулевых слотов. – JLRishe