【问题标题】:Find closest value in an array List with linq?使用linq在数组列表中查找最接近的值?
【发布时间】:2014-11-05 08:58:28
【问题描述】:

我有一个这样的列表:

    public static List<int[]> list = new List<int[]>();

此外,我还有一个名为 X 的变量。X 可以取任何值。我想在list[?][1] 中找到与 X 最接近且更小的值。例如:

如果 X 是 1300,我想获取列表索引:1。或者如果 X 是 700,我想获取索引:0。我如何通过 linq 做到这一点?或者,有没有其他解决方案?

提前致谢。

【问题讨论】:

  • 您要返回什么?指数?价值?
  • (1) 一定要用Linq吗? (2) 正如弗林德伯格所问的,你想要返回什么?内表?内部列表+该列表中找到的元素的索引?外部列表索引 + 到该列表中找到的元素的内部列表索引?还是别的什么?
  • 我想将最接近值的索引设为 X 变量。作为我的问题,如果 X = 700,则结果应该为 0。因为 480 最接近 X 且值更小,并且它的列表索引为 0。

标签: c# linq list


【解决方案1】:

您可以通过以下方式进行操作(sn-p 假设该列表不为空)

var x = 700;
var result = list.Select((subList, idx) => new { Value = subList[1], Idx = idx })
                 .Where(elem => elem.Value < x)
                 .Select(elem => new { Diff = Math.Abs(x - elem.Value), elem.Idx })
                 .OrderBy(elem => elem.Diff).FirstOrDefault();

if (result != null)
{
    return result.Idx;
}

// case - there is no such index

【讨论】:

  • 感谢您的关注。然而,它找到最接近的值但不是更小。例如:X = 3889, list[7][1] = 3630, list[8][1] = 4080。所以,查询找到 8。但它应该是 7。我该怎么办?
  • @team16sah 是的,我没注意到 - 请现在检查(编辑后)
  • 事实上,代码似乎只有在数组中的值从小到大预先排序时才有效。
  • @madd0 数组排序无关紧要。 OP 想要查看数组中的第一个索引。 (列表[?][1])。没有他想在整个数组中搜索的信息。
  • 有问题。结果在我的示例中为 null。
【解决方案2】:

我知道您要求的是 Linq 解决方案,但我认为非 Linq 解决方案也不错。

如果您对非 Linq 解决方案感兴趣,这里有一个(它确实在某个地方使用了 Linq,但实际上这有点牵强!)。

感兴趣的主要方法FindClosestSmaller()返回一个Tuple,其中.Item1是包含小于或等于目标值的最接近值的外部列表的索引,.Item2是该匹配项在内部数组中的索引。

如果没有找到小于或等于目标值的值,.Item1.Item2 都将为零。

请注意,FindClosestSmaller() 采用 IEnumerable&lt;IEnumerable&lt;int&gt;&gt; 类型的参数,这意味着您可以将其与大多数集合类型一起使用,并且不仅限于 List&lt;int[]&gt;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Demo
{
    public static class Program
    {
        private static void Main()
        {
            var ints1 = new [] { 1,  480,  749, 270 };
            var ints2 = new [] { 1,  810, 1080, 271 };
            var ints3 = new [] { 1, 7680, 7949, 271 };

            var intLists = new List<int[]> {ints1, ints2, ints3};

            test(intLists, 1300);
            test(intLists,  700);
            test(intLists,  480);
            test(intLists,    0);
        }

        private static void test(List<int[]> values, int target)
        {
            var result = FindClosestSmaller(values, target);
            Console.WriteLine("Target {0} found: Outer index = {1}, Inner index = {2}", target, result.Item1, result.Item2);
        }

        public static Tuple<int, int> FindClosestSmaller(IEnumerable<IEnumerable<int>> sequences, int target)
        {
            int closest = int.MaxValue;

            int closestInner = 0; // Setting these to zero means we take the first element of the
            int closestOuter = 0; // first list if no smaller element is found.

            int outer = 0;

            foreach (var sequence in sequences)
            {
                int inner = 0;

                foreach (int distance in sequence.Select(value => target - value))
                {
                    if ((distance >= 0) && (distance < closest))
                    {
                        closest      = distance;
                        closestInner = inner;
                        closestOuter = outer;
                    }

                    ++inner;
                }

                ++outer;
            }

            return new Tuple<int, int>(closestOuter, closestInner);
        }
    }
}

【讨论】:

  • +1 用于干净的代码。 Linq 解决方案非常丑陋且难以维护且难以理解,这不是 linq 的地方。
【解决方案3】:

您可以首先将元素展平为一个新的匿名类型,其中index 是外部数组中的索引,item 是内部数组中的值:

假设输入和期望的目标值:

var target = 20;
var input = (new int[][]{new int[]{1,2,3}, new int[]{4,7,8}, new int[]{5,4}});

那么扁平化就是

var tmp = input.SelectMany((x, y) => x.Select(item => 
         new {index = y, item = item, delta = Math.Abs(target - item)}));

现在您可以找到最佳增量:

var bestDelta = tmp.Min(x => x.delta);

由此很容易找到最佳匹配:

var result = tmp.FirstOrDefault(x => x.delta == bestDelta);

或者,如果您更喜欢简单地获取索引:

var index = tmp.Where(x => x.delta == bestDelta).Select(x => x.index).First();

这可以重写为oneliner:

var result = input.SelectMany((x, y) => 
     x.Select(item => new {index = y, item = item, delta = Math.Abs(target - item)}))
     .OrderBy(x => x.delta).Select(x => x.index).First();

但我倾向于发现其他解决方案更具可读性。

【讨论】:

    猜你喜欢
    • 2014-11-12
    • 2014-11-20
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 2015-07-26
    • 2020-02-13
    • 2017-06-25
    • 1970-01-01
    相关资源
    最近更新 更多