Ближайшее значение List<DateTime> через LINQ

199
31 мая 2019, 23:10

прошу помочь составить Linq запрос

List<DateTime> j = new List<DateTime>();

в нем неизвестное кол-во елементов со значением дат. в формате "year-month-day" нужно в новую переменную вытащить дату наиболее близкую к сегодняшней.

тоесть если в листе 2018-11-30, 2018-12-01, 2018-12-02,

то должно вытянуть 2018-12-2

Answer 1

Я думаю, что что-то типа этого:

List<DateTime> j = new List<DateTime>();
var nowDate=DateTime.Now;
j.Select(date => new {Secs = Math.Abs((nowDate - date).TotalSeconds), Date = date})
                        .OrderBy(x => x.Secs).First();

Логика

Я вычитаю из текущий даты, дату каждого элемента, а затем перевожу в секунды.

На основании этого я делаю анонимный класс Secs-секунды и Date-DateTime из вашего списка.

Далее, я сортирую по секундам по возрастанию. Очевидно, что элемент, где меньше всего секунд и будет нужным нам элементом. Так как мы выполнили сортировку, то он должен быть первым.

А возвращает 2 значения из-за того, что был создан анонимный класс с вспомогательным полем по которому мы выполнили сортировку. Можно просто взять элемент DateTime и присвоить в другую переменную.

Как альтернативный вариант, можно сделать так:

 List<DateTime> j = new List<DateTime>();
 var nowDate=DateTime.Now;
j.OrderBy(date => Math.Abs((nowDate - date).TotalSeconds)).First();
Answer 2

Альтернативным решением может быть использование метода Aggregate, это позволит найти значение с минимальным отклонением за один проход.

Для этого нужно задать начальное состояние, в котором хранить выбранную дату и ее разницу с Now.

new { Diff = TimeSpan.MaxValue, Date = new DateTime(0) }

В самом методе необходимо вычислить разницу между текущим элементом и Now, и получить ее абсолютное значение. Для этого можно воспользоваться методом .Duration

var diff = (cur - now).Duration();

Если полученная разница больше сохраненной, вернуть сохраненную, если меньше - вернуть новую.

return (diff <= acc.Diff) ? new { Diff = diff, Date = cur } : acc

Чтобы в результате получить только дату, нужно добавить третий параметр, который показывает как определять результат, в данном случае нужно просто вернуть поле Date

acc => acc.Date

Все в сборе может выглядеть так:

var now = DateTime.UtcNow;
var result = j.Aggregate(new { Diff = TimeSpan.MaxValue, Date = new DateTime(0) }, (acc, cur) =>
  {
      var diff = (cur - now).Duration();
      return (diff <= acc.Diff) ? new { Diff = diff, Date = cur } : acc
  }, acc => acc.Date);
Answer 3

Есть популярная библиотека MoreLinq, с большим набором методов расширений. Предлагаю воспользоваться ей: решение будет лаконичным и понятным.

using MoreLinq;

var now = DateTime.Now;
var near = j.MinBy(date => Math.Abs((now - date).Ticks)).First();

По сути это тот же алгоритм, что в ответе iluxa1810.
И я думаю, что лучше использовать целочисленное свойство Ticks.

Answer 4
     var now = DateTime.Now;
     j.Aggregate(DateTime.MaxValue, (acc, next) => {
        var prevDiff = Math.Abs((now - acc).TotalHours);
        var currentDiff = Math.Abs((now - next).TotalHours);
        return prevDiff <= currentDiff ? acc : next;
      });
READ ALSO
Неясное поведение атрибута OptionalAttribute

Неясное поведение атрибута OptionalAttribute

Для начала приведу простой пример кода:

210
FileStream и метод Seek

FileStream и метод Seek

Как при помощи метода FileStreamSeek считать часть массива байт от всего массива?

147
Переходы между уровней

Переходы между уровней

Всем привет, наверно я уже надоел) задаю и задаю вопросыЯ и не знаю как правильно задать этот вопрос

198
Занести данные в SQL циклом

Занести данные в SQL циклом

Можете подсказать как занести данные в SQL цикломНапример: я получаю циклом данные из 1С и эти данные занести в SQL

135