繁体   English   中英

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

[英]Find closest value in an array List with linq?

我有这样一个列表:

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

在此输入图像描述

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

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

提前致谢。

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

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

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

如果你对非Linq解决方案感兴趣,那么就是一个(它确实在一个地方使用了Linq,但实际上这是非常重要的!)。

感兴趣的主要方法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);
        }
    }
}

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM