2010-09-16 113 views
28

我有一个List,MyStuff具有Type Float属性。LINQ获取最接近的值?

有属性值为10,20,22,30的对象。

我需要编写一个查询,查找最接近21的对象,在这种情况下,它会找到20和22对象。然后,我需要写一个发现对象关闭到21而不会超过,并将返回值为20的对象。

我不知道在哪里/如何从这一开始。帮帮我?

谢谢。

更新 - 哇有这么多真棒回应在这里。谢谢!我不知道哪一个要遵循,所以我会尝试一切。有一件事可能会让这个问题变得更有趣或者更少,因为相同的查询将不得不适用于LINQ-to-SQL实体,所以从MS Linq论坛收获的答案可能会最好?不知道。

+0

呃,22是21岁以上....当然会找到20吗? – cjk 2010-09-16 11:50:52

+0

是的,我的意思是20,对于拧紧抱歉。 – Snowy 2010-09-16 15:02:22

回答

18

下面是满足线性时间的第二个查询的解决方案:

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

(来自“上面”编辑,以“自下而上”的澄清之后)

至于第一个查询,这将是最容易使用MoreLinqMinBy扩展:

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)为OT的第一个值她已经发布。

+0

如果我们需要22而不是? – deadManN 2016-08-02 09:15:40

21

尝试用数字和21,然后采取的第一项之差的绝对值,对它们进行排序:根据@Yuriy Faktorovich的评论

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

或缩短它:

float closest = MyStuff 
    .OrderBy(n => Math.Abs(n - 21)) 
    .First(); 
+5

您可以通过删除“Select”并将距离放入'OrderBy'来缩短该距离。 – 2010-09-16 02:47:13

6

基于微软Linq论坛上的this post

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); 
2
List<float> numbers = new List<float>() { 10f, 20f, 22f, 30f }; 
float pivot = 21f; 
var result = numbers.Where(x => x >= pivot).OrderBy(x => x).FirstOrDefault(); 

OR

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); 
+1

您应该在'Where'后面放置'OrderBy' *,以便它不必排序尽可能多的元素。 – Gabe 2010-09-16 02:58:58

+0

@加贝 - 感谢您的建议。我修改了代码。 – 2010-09-16 03:06:45