У меня есть объект Person со значением Nullable DateOfBirth. Есть ли способ использовать LINQ для запроса списка объектов Person для объекта с самым ранним / наименьшим значением DateOfBirth.
Вот с чего я начал:
var firstBornDate = People.Min(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue));
Нулевым значениям DateOfBirth присвоено значение DateTime.MaxValue, чтобы исключить их из минимального рассмотрения (при условии, что хотя бы у одного указан указанный DOB).
Но все, что мне нужно, это установить firstBornDate в значение DateTime. Я хотел бы получить объект Person, который соответствует этому. Нужно ли мне написать второй запрос так:
var firstBorn = People.Single(p=> (p.DateOfBirth ?? DateTime.MaxValue) == firstBornDate);
Или есть более простой способ сделать это?
a.Min(x => x.foo);
max("find a word of maximal length in this sentence".split(), key=len)
возвращает строку «предложение». В C #"find a word of maximal length in this sentence".Split().Max(word => word.Length)
высчитывает , что 8 является самой длинной длиной любого слова, но не сказать вам , что самое длинное слово есть .Ответы:
источник
curMin == null
?curMin
может быть толькоnull
если вы используетеAggregate()
с семенем, которое естьnull
.К сожалению, для этого нет встроенного метода, но его достаточно легко реализовать для себя. Вот его внутренности:
Пример использования:
Обратите внимание, что это вызовет исключение, если последовательность пуста, и вернет первое элемент с минимальным значением, если их больше одного.
В качестве альтернативы вы можете использовать реализацию, которую мы получили в MoreLINQ , в MinBy.cs . (Там есть соответствующий
MaxBy
, конечно.)Установить через консоль диспетчера пакетов:
источник
ПРИМЕЧАНИЕ. Я включил этот ответ для полноты, поскольку в ОП не упоминается, что является источником данных, и мы не должны делать никаких предположений.
Этот запрос дает правильный ответ, но может быть медленнее, поскольку может потребоваться отсортировать все элементы в
People
зависимости от структуры данныхPeople
:ОБНОВЛЕНИЕ: На самом деле я не должен называть это решение «наивным», но пользователь должен знать, к чему он обращается. «Медлительность» этого решения зависит от базовых данных. Если это массив или
List<T>
, то у LINQ to Objects нет другого выбора, кроме как сначала отсортировать всю коллекцию перед выбором первого элемента. В этом случае это будет медленнее, чем предложенное другое решение. Однако, если это таблица LINQ to SQL иDateOfBirth
индексированный столбец, SQL Server будет использовать индекс вместо сортировки всех строк. Другие пользовательскиеIEnumerable<T>
реализации могут также использовать индексы (см. I4o: Indexed LINQ или объектную базу данных db4o ) и сделать это решение быстрее, чемAggregate()
илиMaxBy()
/MinBy()
которые нужно перебрать всю коллекцию один раз. На самом деле, LINQ to Objects (теоретически) мог бы создать особые случаиOrderBy()
для отсортированных коллекций, какSortedList<T>
, но, насколько я знаю, это не так.источник
Сделал бы трюк
источник
Итак, вы просите
ArgMin
илиArgMax
. C # не имеет встроенного API для тех.Я искал чистый и эффективный (O (N) вовремя) способ сделать это. И я думаю, что нашел один:
Общая форма этого шаблона:
Специально, используя пример в оригинальном вопросе:
Для C # 7.0 и выше, который поддерживает кортеж значения :
Для версии C # до 7.0 вместо этого может использоваться анонимный тип :
Они работают , потому что оба значения кортежа и анонимный тип имеют осмысленные компараторов по умолчанию: для (x1, y1) и (x2, y2), она сначала сравнивает
x1
противx2
, тоy1
противy2
. Вот почему встроенный.Min
может быть использован на этих типах.А так как анонимный тип и кортеж значения являются типами значения, они должны быть оба очень эффективными.
НОТА
В моих вышеприведенных
ArgMin
реализациях я предполагалDateOfBirth
взять типDateTime
для простоты и ясности. Исходный вопрос просит исключить эти записи с нулевымDateOfBirth
полем:Это может быть достигнуто с предварительной фильтрацией
Так что это несущественно в вопросе реализации
ArgMin
илиArgMax
.ЗАМЕТКА 2
Приведенный выше подход имеет оговорку, что, когда два экземпляра имеют одинаковое минимальное значение,
Min()
реализация попытается сравнить эти экземпляры как прерыватели связей. Однако, если класс экземпляров не реализуетсяIComparable
, будет выдана ошибка времени выполнения:К счастью, это все еще можно исправить довольно чисто. Идея состоит в том, чтобы связать отдаленный «идентификатор» с каждой записью, которая служит однозначным нарушителем связей. Мы можем использовать инкрементный идентификатор для каждой записи. Все еще используя возраст людей в качестве примера:
источник
Решение без дополнительных пакетов:
также вы можете обернуть его в расширение:
и в этом случае:
Кстати ... O (n ^ 2) не лучшее решение. Пол Беттс дал более полное решение, чем мой. Но мое по-прежнему решение LINQ, и оно здесь более простое и короткое, чем другие решения.
источник
источник
Совершенно простое использование агрегата (эквивалентно сложению на других языках):
Единственный недостаток - доступ к свойству дважды для каждого элемента последовательности, что может быть дорого. Это трудно исправить.
источник
Ниже приведено более общее решение. По сути, он делает то же самое (в порядке O (N)), но для любых типов IEnumberable и может смешиваться с типами, чьи селекторы свойств могут возвращать нуль.
тесты:
источник
РЕДАКТИРОВАТЬ снова:
Сожалею. Помимо того, что мне не хватало значения, я искал не ту функцию,
Min <(Of <(TSource, TResult>)>) (IEnumerable <(Of <(TSource>)>), Func <(Of <(TSource, TResult>)>)) действительно возвращает тип результата, как вы сказали.
Я бы сказал, что одним из возможных решений является реализация IComparable и использование Min <(Of <(TSource>)>) (IEnumerable <(Of <(TSource>)>)) , который действительно возвращает элемент из IEnumerable. Конечно, это не поможет вам, если вы не можете изменить элемент. Я нахожу дизайн MS немного странным здесь.
Конечно, вы всегда можете сделать цикл for, если вам нужно, или использовать реализацию MoreLINQ, которую дал Джон Скит.
источник
Другая реализация, которая может работать с обнуляемыми клавишами выбора и для коллекции ссылочного типа, возвращает нуль, если подходящих элементов не найдено. Это может быть полезно, например, для обработки результатов базы данных.
Пример:
источник
Попробуйте следующую идею:
источник
Я сам искал что-то подобное, желательно без использования библиотеки или сортировки всего списка. Мое решение оказалось похожим на сам вопрос, только немного упрощенным.
источник
var min = People.Min(...); var firstBorn = People.FirstOrDefault(p => p.DateOfBirth == min...
В противном случае он получает мин несколько раз, пока не найдет тот, который вы ищете.