LINQ, чтобы получить самое близкое значение?
У меня есть List, MyStuff имеет свойство Type Float.
Существуют объекты со значениями свойств 10,20,22,30.
Мне нужно написать запрос, который найдет объекты, наиболее близкие к 21, в этом случае он найдет объект 20 и 22. Затем мне нужно написать тот, который обнаруживает, что объект закрывается до 21, не перейдя, и он вернет объект со значением 20.
Я понятия не имею, где и как начать с этого. Помощь?
Спасибо.
Update - ничего страшного здесь. Благодарю! Я не знаю, за кем следовать, поэтому я попробую их всех. Одна вещь, которая может сделать это более (или менее) интересным, заключается в том, что тот же запрос должен применяться к объектам LINQ-to-SQL, поэтому, возможно, ответ, собранный с форумов MS Linq, будет работать лучше всего? Не знаю.
Ответы
Ответ 1
Здесь решение, удовлетворяющее второму запросу в линейном времени:
var pivot = 21f;
var closestBelow = pivot - numbers.Where(n => n <= pivot)
.Min(n => pivot - n);
(Отредактировано от "выше" до "ниже" после уточнения)
Что касается первого запроса, было бы проще использовать MoreLinq
MinBy
extension:
var closest = numbers.MinBy(n => Math.Abs(pivot - n));
Это также можно сделать в стандартном LINQ в линейном времени, но с 2 проходами источника:
var minDistance = numbers.Min(n => Math.Abs(pivot - n));
var closest = numbers.First(n => Math.Abs(pivot - n) == minDistance);
Если эффективность не является проблемой, вы можете отсортировать последовательность и выбрать первое значение в O(n * log n)
, как опубликовали другие.
Ответ 2
Попробуйте отсортировать их по абсолютной величине разницы между числом и 21, а затем возьмите первый элемент:
float closest = MyStuff
.Select (n => new { n, distance = Math.Abs (n - 21) })
.OrderBy (p => p.distance)
.First().n;
Или сократите его в соответствии с комментарием @Yuriy Faktorovich:
float closest = MyStuff
.OrderBy(n => Math.Abs(n - 21))
.First();
Ответ 3
На основе этот пост на форумах Microsoft Linq:
var numbers = new List<float> { 10f, 20f, 22f, 30f };
var target = 21f;
//gets single number which is closest
var closest = numbers.Select( n => new { n, distance = Math.Abs( n - target ) } )
.OrderBy( p => p.distance )
.First().n;
//get two closest
var take = 2;
var closests = numbers.Select( n => new { n, distance = Math.Abs( n - target ) } )
.OrderBy( p => p.distance )
.Select( p => p.n )
.Take( take );
//gets any that are within x of target
var within = 1;
var withins = numbers.Select( n => new { n, distance = Math.Abs( n - target ) } )
.Where( p => p.distance <= within )
.Select( p => p.n );
Ответ 4
List<float> numbers = new List<float>() { 10f, 20f, 22f, 30f };
float pivot = 21f;
var result = numbers.Where(x => x >= pivot).OrderBy(x => x).FirstOrDefault();
ИЛИ
var result = (from n in numbers
where n>=pivot
orderby n
select n).FirstOrDefault();
и здесь идет метод расширения:
public static T Closest<T,TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, TKey pivot) where TKey : IComparable<TKey>
{
return source.Where(x => pivot.CompareTo(keySelector(x)) <= 0).OrderBy(keySelector).FirstOrDefault();
}
Использование:
var result = numbers.Closest(n => n, pivot);