LINQ, чтобы получить ближайшее значение? - PullRequest
32 голосов
/ 16 сентября 2010

У меня есть список, MyStuff имеет свойство типа Float.

Существуют объекты со значениями свойств 10,20,22,30.

Мне нужно написать запрос, который находит объекты, ближайшие к 21, в этом случае он найдет объекты 20 и 22. Затем мне нужно написать тот, который находит, что объект закрывается до 21 без перехода, и он возвратил бы объект со значением 20.

Понятия не имею, где / как начать с этого. Помощь

Спасибо.

Обновление - вау, здесь так много классных ответов. Спасибо! Я не знаю, за кем следовать, поэтому попробую их все. Одна вещь, которая может сделать это более (или менее) интересным, заключается в том, что один и тот же запрос придется применять к сущностям LINQ-to-SQL, поэтому, возможно, ответ, полученный на форумах MS Linq, будет работать лучше? Не знаю.

Ответы [ 4 ]

24 голосов
/ 16 сентября 2010

Попробуйте отсортировать их по абсолютной величине разности между числом и 21, а затем возьмите первый элемент:

float closest = MyStuff
    .Select (n => new { n, distance = Math.Abs (n - 21) })
    .OrderBy (p => p.distance)
    .First().n;

Или сократить в соответствии с комментарием @Yury Faktorovich:

float closest = MyStuff
    .OrderBy(n => Math.Abs(n - 21))
    .First();
22 голосов
/ 16 сентября 2010

Вот решение, которое удовлетворяет второму запросу за линейное время:

var pivot = 21f;
var closestBelow = pivot - numbers.Where(n => n <= pivot)
                                  .Min(n => pivot - n);

(отредактировано «сверху» до «снизу» после уточнения)

Что касается первого запроса, то онобыло бы проще использовать расширение MoreLinq MinBy:

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), как и другие.

7 голосов
/ 16 сентября 2010

На основании этого сообщения на форумах 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 );
3 голосов
/ 16 сентября 2010
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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...