【问题标题】:C# chunking two-dimensional array into batchesC# 将二维数组分块成批处理
【发布时间】:2019-03-29 13:51:27
【问题描述】:

我有一个二维 object[,] 数组,其中包含行和列矩阵 (object[nRows, nColumns])。

我想把它分成一批行 - 例如每批 1,000 行,我可以枚举。

总而言之,我正在寻找可以执行以下操作但针对二维数组 (source) 的 C# 代码:

private IEnumerable<T[]> SplitArray<T>(T[] sourceArray, int rangeLength)
{
    int startIndex = 0;

    do
    {
        T[] range = new T[Math.Min(rangeLength, sourceArray.Length - startIndex)];
        Array.Copy(sourceArray, startIndex, range, 0, range.Length);
        startIndex += rangeLength;
        yield return range;
    }
    while (startIndex < sourceArray.Length);            
}

这种为 [,] 数组调整代码的尝试失败了 - 在第一次迭代后行/列开始变得混乱:

        private IEnumerable<T[,]> SplitArray<T>(T[,] sourceArray, int rangeLength)
        {
            int startIndex = 0;

            do
            {
                T[,] range = new T[Math.Min(rangeLength, sourceArray.GetLength(0) - startIndex), sourceArray.GetLength(1)];
                Array.Copy(sourceArray, startIndex, range, 0, range.Length);
                startIndex += rangeLength;
                yield return range;
            }
            while (startIndex < sourceArray.GetLength(0));
        }

【问题讨论】:

  • 是什么阻止您独立枚举二维数组中的不同行?应该没有必要仅仅为此目的创建阵列的(部分)克隆。 (另一方面,如果拆分和创建新数组是您想要的,那么您将问题中的代码调整为 2D [,] 数组的问题究竟是什么?)
  • 谢谢!它们需要被分块,因为它们必须通过 COM 互操作批量处理到另一个应用程序中。单行会导致调用过多,一次执行所有行会导致内存不足。我尝试调整代码更新了这个问题。
  • 我对你的理解正确吗 - 你想在“方形”子数组(块)中分解一个二维数组吗?你想如何处理边缘/剩菜?
  • 请注意,Array.Copy 将 2D 数组视为 1D 数组(其中 2D 数组的元素以行优先顺序出现)。因此,startIndex 的值与 1st rank 的长度(即行的长度)无关。这意味着,像您在那里所做的那样组合 sourceArray.GetLength(0) - startIndex 几乎没有意义。此外,startIndex += rangeLength 也没有多大意义,因为您只是复制了 range.Length 元素,而不是 rangeLength 元素(因为您似乎仅将 rangeLength 用作二维中一维的范围数组)
  • @BartvanderDrift 我认为这就是我遇到的问题。每个块应该是一组独立的行,其所有列都保持不变 - 如果我有任何意义的话。

标签: c# arrays multidimensional-array


【解决方案1】:

这将解决您的代码问题。由于 Array.Copy 威胁将数组视为一维,因此您必须乘以列数才能获得某些地方的元素总数:

private IEnumerable<T[,]> SplitArray<T>(T[,] sourceArray, int rangeLength)
{
    int startIndex = 0;
    do
    {
        T[,] range = new T[Math.Min(rangeLength, sourceArray.GetLength(0) - startIndex/sourceArray.GetLength(1)), sourceArray.GetLength(1)];
        Array.Copy(sourceArray, startIndex, range, 0, range.Length);
        startIndex += rangeLength*sourceArray.GetLength(1);
        yield return range;
    }
    while (startIndex < sourceArray.Length);
}

【讨论】:

    【解决方案2】:

    通过使用GetLength(int dimension),您可以查看数组的特定维度有多长,然后对其进行迭代。您还需要将其他维度作为常量,并确保整个事物与 Array.Rank 值匹配。从那里,只需通过Array.GetValue(int[]) 查找值。这可能有点困难,因为 Array 不是通用的:

    public static IEnumerable<T> GetRow<T>(this Array source, int dimension, params int[] fixedDimensions)
    {
        if(source == null) throw new ArgumentNullException(nameof(source));
        if(!typeof(T).IsAssignableFrom(source.GetType().GetElementType()) throw new OperationException($"Cannot return row of type {typeof(T)} from array of type {source.GetType().GetElementType()}");
    
        if(fixedDimensions == null) fixedDimensions = new T[0];
        if(source.Rank != fixedDimensions.Length + 1) throw new ArgumentException("Fixed dimensions must have exactly one fewer elements than dimensions in source", nameof(fixedDimensions));
        if(dimension > source.Rank) throw new ArgumentException($"Cannot take dimension {dimension} of an array with {source.Rank} dimensions!", nameof(dimension));
        if(dimension < 0) throw new ArgumentException("Cannot take a negative dimension", nameof(dimension));
    
        var coords = dimension == source.Rank
             ? fixedDimensions
                .Concat(new [] { 0 })
                .ToArray()
            : fixedDimensions
                .Take(dimension)
                .Concat(new [] { 0 })
                .Concat(fixedDimensions.Skip(dimension))
                .ToArray();
    
        var length = source.GetLength(dimension);
        for(; coords[dimension] < length; coords[dimension]++)
        {
            yield return (T)source.GetValue(coords);
        }
    }
    

    【讨论】:

      【解决方案3】:

      我认为您正在寻找这样的东西:

      private static List<T[]> SplitArray<T>(T[,] sourceArray)
      {
          List<T[]> result = new List<T[]>();
          int rowCount = sourceArray.GetLength(0);
          for (int i = 0; i < rowCount; i++)
          {
              result.Add(GetRow(sourceArray, i));
          }
      
          return result;
      }
      
      private static T[] GetRow<T>(T[,] sourceArray, int rownumber)
      {
          int columnCount = sourceArray.GetLength(1);
          var row = new T[columnCount];
          for (int i = 0; i < columnCount; i++)
          {
              row[i] = sourceArray[rownumber, i];
          }
          return row;
      }
      

      【讨论】:

        猜你喜欢
        • 2018-11-02
        • 2018-05-03
        • 1970-01-01
        • 2014-07-13
        • 2014-09-25
        • 1970-01-01
        • 2022-11-29
        • 1970-01-01
        相关资源
        最近更新 更多