使用LINQ (c#)对n维数组进行数组操作
本文关键字:数组 操作 LINQ 使用 | 更新日期: 2023-09-27 18:07:49
假设我们有一个锯齿数组
int[][] a = { new[] { 1, 2, 3, 4 }, new[] { 5, 6, 7, 8 }, new[] { 9, 10, 11, 12 } };
要得到第二行和第二列的和,可以分别写两行代码:
int rowSum = a[1].Sum();
int colSum = a.Select(row => row[1]).Sum();
但是如果我们有二维数组的定义
int[,] a = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
由于编译器错误,上面引用的代码将无法工作:
Error 1 Wrong number of indices inside []; expected 2
Error 2 'int[*,*]' does not contain a definition for 'Select' and no extension method 'Select' accepting a first argument of type 'int[*,*]' could be found (are you missing a using directive or an assembly reference?)
那么,问题是:如何在n维数组中使用LINQ方法,而不是锯齿数组?在哪里有一个方法来转换矩形数组到锯齿形?注:我试图在文档中找到答案,但没有结果。
LINQ to Objects是基于IEnumerable<接口,即值的一维序列。这意味着它不能很好地与n维数据结构(如非锯齿数组)混合,尽管这是可能的。>
您可以生成索引到n维数组的一维整数序列:
int rowSum = Enumerable.Range(0, a.GetLength(1)).Sum(i => a[1, i]);
int colSum = Enumerable.Range(0, a.GetLength(0)).Sum(i => a[i, 1]);
关于您的问题"如何使用n维数组LINQ方法":
你不能对n维数组使用大多数LINQ方法,因为这样的数组只实现IEnumerable
而不实现IEnumerable<T>
,并且大多数LINQ扩展方法是IEnumerable<T>
的扩展方法。
关于另一个问题:参见dtb的回答
添加到dtb的解决方案中,遍历数组所有项的更通用的方法是:
int[,] b = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
var flattenedArray = Enumerable.Range(0, b.GetLength(0))
.SelectMany(i => Enumerable.Range(0, b.GetLength(1))
.Select(j => new { Row = i, Col = j }));
现在:
var rowSum2 = flattenedArray.Where(t => t.Row == 1).Sum(t => b[t.Row, t.Col]);
var colSum2 = flattenedArray.Where(t => t.Col == 1).Sum(t => b[t.Row, t.Col]);
当然,这是非常浪费的,因为我们正在创建坐标元组,即使是那些我们最终将用Where
过滤掉的项目,但是如果你不知道事先的选择标准是什么,这就是要走的路(或者不是——这看起来更像是一个练习,而不是你想在实践中做的事情)。
我还可以想象如何使用递归lambda和Tuple
将其扩展到任何秩的数组(不仅仅是2D),但这跨越了受虐的领域。
2D数组没有任何内置的遍历行或列的方式。不过,创建自己的这种方法并不太难。有关获取行和列的可枚举对象的实现,请参阅此类。
public static class LINQTo2DArray
{
public static IEnumerable<T> Row<T>(this T[,] Array, int Row)
{
for (int i = 0; i < Array.GetLength(1); i++)
{
yield return Array[Row, i];
}
}
public static IEnumerable<T> Column<T>(this T[,] Array, int Column)
{
for (int i = 0; i < Array.GetLength(0); i++)
{
yield return Array[i, Column];
}
}
}
你也可以使用a.Cast<int>()
来平化数组,但是你会丢失所有关于列/行的信息
更简单的方法是像下面这样做
var t = new List<Tuple<int, int>>();
int[][] a = t.Select(x => new int[]{ x.Item1, x.Item2}).ToArray();
我能看到的在二维数组上执行这些行和列操作的最简单的LINQ方法是定义以下查找:
var cols = a
.OfType<int>()
.Select((x, n) => new { x, n, })
.ToLookup(xn => xn.n % a.GetLength(1), xn => xn.x);
var rows = a
.OfType<int>()
.Select((x, n) => new { x, n, })
.ToLookup(xn => xn.n / a.GetLength(1), xn => xn.x);
现在你可以简单地这样做:
var firstColumnSum = cols[0].Sum();
对于n维,这太痛苦了…对不起。