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

3

我有一个这样的列表:

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

输入图像描述

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

如果X是1300,则要取列表索引:1。或者如果X是700,则要取索引:0。如何通过LINQ实现这一点?或者,是否有其他解决方案?

提前感谢。


你想要返回什么?索引?还是值? - flindeberg
(1) 需要使用Linq吗? (2) 就像flindeberg问的那样,你想返回什么?内部列表?内部列表+在该列表中找到的元素索引?外部列表索引+内部列表索引到在该列表中找到的元素?还是其他什么? - Matthew Watson
我想将最接近的值的索引赋给变量X。根据我的问题,如果X = 700,则结果应为0。因为480是最接近且小于X的值,它的列表索引为0。 - 1teamsah
3个回答

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

感谢您的关注。但是它找到的是最接近的值而不是更小的值。例如:X = 3889,list [7] [1] = 3630,list [8] [1] = 4080。因此,查询会找到8。但应该是7。我该怎么办? - 1teamsah
1
目前代码似乎只在数组中的值预先按从小到大排序时才能正常工作。 - madd0
@madd0 数组排序无关紧要。OP想要查看数组中第一个索引(list [?] [1])的内容。没有信息表明他想在整个数组中搜索。 - user2160375
尽管X = 480且list[0][1] = 480,结果仍为空。 - 1teamsah
@team16sah,你写的是“小于”,而不是“小于等于”。如果你想包含等于,只需将 elem.Value < x 改为 elem.Value <= x - user2160375
显示剩余4条评论

1

我知道你要求使用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);
        }
    }
}

+1 代表干净的代码。Linq 解决方案非常丑陋,难以维护和理解,这不是使用 Linq 的地方。 - Erti-Chris Eelmaa

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

这可以重写为一行代码:
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();

但我倾向于认为另一种解决方案更易读。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接