我有一个这样的列表:
public static List<int[]> list = new List<int[]>();
此外,我还有一个名为X的变量。X可以取任何值。我想在list [?] [1]
中找到最接近且小于X的值。例如:
如果X是1300,则要取列表索引:1。或者如果X是700,则要取索引:0。如何通过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
elem.Value < x
改为 elem.Value <= x
。 - user2160375我知道你要求使用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)}));
现在你可以找到最优的 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();
但我倾向于认为另一种解决方案更易读。