使用linq在数组列表中查找最接近的值?

时间:2014-11-05 08:58:28

标签: c# linq list

我有这样一个列表:

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

enter image description here

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

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

提前致谢。

3 个答案:

答案 0 :(得分:4)

您可以通过以下方式执行此操作(代码段假设,该列表不为空)

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

答案 1 :(得分:1)

我知道您要求Linq解决方案,但我认为非Linq解决方案也很好。

如果您对非Linq解决方案感兴趣,可以选择其中一个( 在一个地方使用Linq,但实际上,这一点非常重要!)。< / p>

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

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

请注意,FindClosestSmaller()采用IEnumerable<IEnumerable<int>>类型的参数,这意味着您可以将其用于大多数集合类型,并且您不仅限于List<int[]>

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);
        }
    }
}

答案 2 :(得分:0)

您可以从将元素展平为新的匿名类型开始,其中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)}));

现在你可以找到最佳的delta:

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();

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