【问题标题】:Array operations with n-dimensional array using LINQ (C#)使用 LINQ (C#) 对 n 维数组进行数组操作
【发布时间】:2011-11-11 05:55:56
【问题描述】:

假设我们有一个锯齿状数组

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?)

那么,问题来了:如何将 LINQ 方法用于 n 维数组,而不是锯齿状数组?以及将矩形数组转换为锯齿状的方法在哪里?

附:我试图在文档中找到答案,但没有结果。

【问题讨论】:

  • C# 并不真正(广泛)支持多维数组 :-(

标签: c# arrays linq


【解决方案1】:

LINQ to Objects 基于IEnumerable<T> Interface,即一维值序列。这意味着它不能很好地与非锯齿状数组等 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]);

【讨论】:

    【解决方案2】:

    关于您的问题“如何在 n 维数组中使用 LINQ 方法”:

    您不能将大多数 LINQ 方法与 n 维数组一起使用,因为这样的数组仅实现 IEnumerable 而不是 IEnumerable&lt;T&gt;,而且大多数 LINQ 扩展方法都是 IEnumerable&lt;T&gt; 的扩展方法。

    关于另一个问题:参见 dtb 的回答。

    【讨论】:

      【解决方案3】:

      要添加到 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 之类的东西将其扩展到任何等级(不仅仅是二维)的数组,但这会跨越到受虐狂领域。

      【讨论】:

        【解决方案4】:

        二维数组没有任何内置的迭代行或列的方式。不过,创建自己的这种方法并不难。有关获取行和列的可枚举的实现,请参见此类。

        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&lt;int&gt;() 将数组展平,但您会丢失有关列/行的所有信息

        【讨论】:

          【解决方案5】:

          一种更简单的方法如下所示

           var t = new List<Tuple<int, int>>();
           int[][] a = t.Select(x => new int[]{ x.Item1, x.Item2}).ToArray(); 
          

          【讨论】:

            【解决方案6】:

            在二维数组上执行此类行和列操作的最简单的仅 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维,太痛苦了……对不起。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-01-24
              • 2019-03-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多