【问题标题】:LINQ to Get Closest Value?LINQ 获得最接近的价值?
【发布时间】:2011-04-13 00:09:05
【问题描述】:

我有一个 List,MyStuff 有一个 Float 类型的属性。

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

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

我不知道从哪里/如何开始。帮忙?

谢谢。

更新 - 哇,这里有这么多很棒的回复。谢谢!我不知道该跟随哪一个,所以我将尝试所有这些。可能使这更多(或更少)有趣的一件事是,相同的查询必须适用于 LINQ-to-SQL 实体,因此从 MS Linq 论坛获得的答案可能效果最好?不知道。

【问题讨论】:

  • 呃,22 超过 21.... 肯定会找到 20?
  • 是的,我的意思是 20,抱歉搞砸了。

标签: c# linq listview object


【解决方案1】:

尝试按数字与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();

【讨论】:

  • 您可以通过删除 Select 并将距离放入 OrderBy 来缩短它
  • 如果 MyStuff 已排序,这可以在 O(n) 中完成,而 OrderBy 不是。如果您的列表已经排序并且大小不是微不足道的(或者这可能会在紧密循环中执行),则需要考虑一些事情。
【解决方案2】:

这是一个在线性时间内满足第二个查询的解决方案:

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) 中的第一个值。

【讨论】:

  • 如果我们需要 22 来代替呢?
【解决方案3】:

基于 Microsoft 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 );

【讨论】:

    【解决方案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);
    

    【讨论】:

    • 您应该将OrderBy 放在 Where 之后,这样它就不必对那么多元素进行排序。
    • @Gabe - 感谢您的建议。我已经修改了代码。
    猜你喜欢
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-02
    • 1970-01-01
    • 2018-08-28
    • 2020-03-20
    相关资源
    最近更新 更多