首页 > 解决方案 > 如何将多个数组(表示数据列)组合成一个嵌套/锯齿状数组(甚至是二维)数组

问题描述

我有一个正在编写的程序,用于从各种 Excel 电子表格中提取某些数据。

到目前为止的过程是:

对于每个确定的电子表格:

- 使用 interop excel 将数据作为多维数组读取(尽管速度很慢,但由于我需要读取所有不同的文件格式,它是最佳选择)

    Sample: object[,] cellValues = (object[,])range.Value2;

- 确定我实际需要的列以及我需要它们的顺序。这存储在一个锯齿状的字节数组中:

    byte[][] targetColumns

- 锯齿状数组本质上是 (columnIndexFromSpreadsheet, preferredColumnOrder) 例如,如果电子表格中的第一列应该作为第 10 列读入,它将是 (1, 10)

-我按首选列顺序对锯齿状数组进行排序(这样我就可以按该顺序循环遍历数组并提取这些列):

        public static byte[][] SortTargetColumns(byte[][] targetColumns)
        {
            return targetColumns.OrderBy(x => x.Skip(1).First()).ToArray();
        }

- 然后我通过从多维数组的该列索引创建一个数组来提取该列。这是被调用的方法:

    public static object[]  ExtractColumn(object[,] dataArray ,byte columnIndex)
    {
        return Enumerable.Range(ArrayIndexStart, dataArray.GetLength(0)).Select(x => dataArray[x, columnIndex]).ToArray();
    }

    Usage:

    array = ExtractColumn(dataArray, (byte) colIndex);

现在我正在尝试将这些提取的数组重新组合在一起以使其可读。我需要对某些列进行一些操作,然后在合并后写入文本文件。唯一的问题是我不知道如何正确地做到这一点。我尝试了以下方法,但继续得到空引用异常:

// Get Row Count of dataArray
int rowCount = dataArray.GetLength(0);

// Create List to store extracted arrays
List<object[]> extractedDataList = new List<object[]>();

// Loop through target columns and extract the column as an array
for (byte colIndex = 1; colIndex <= targetColumns.Length + 1; colIndex++)
{
    object[] array = ExtractColumn(dataArray, (byte) colIndex);
    extractedDataList.Add(array);
}

// Create jagged array
object[][] extractedDataArray = new object[rowCount][] ; 


for(int i = 0; i < extractedDataArray.GetLength(0); i++)
{
    List<object> row = new List<object>();
    for (int j = 0; j < extractedDataList.Count; j++)
    {
        row.Add(extractedDataList[j][i].ToString());
        //extractedDataArray[i][j] = extractedDataList[j][i].ToString(); <-- null reference
    }
    extractedDataArray[i] = row.ToArray();
}

我不知道还有什么可以尝试将这些列数组放回我可以轻松使用的形式。任何和所有提示/建议将不胜感激。

标签: c#.net

解决方案


每当您像这样感到困惑时,请将问题分解为小块,并使用有意义的名称。

假设您有一个列数组,每一列每行有一个元素。可以这样声明:

object[][] columns;

首先,让我们获取行数和列数:

var columnCount = columns.Length;
var rowCount = columns[0].Length;

现在编写一个小的本地函数来接受行和列索引并返回正确的单元格。如果不是所有列的行数都相同,则可以包括边界检查,如果单元格不存在则返回 null。

object Getter(int row, int col)
{
    bool outOfBounds = (row >= columns[col].Length);
    return outOfBounds ? null : columns[col][row];
}

现在我们要做的就是遍历行来创建内部数组:

object[][] target = new object[rowCount][]
for (int row = 0; row < rowCount; row++)
{
    target[row] = new object[columnCount];
}

并添加使用 getter 填充单元格的代码:

object[][] target = new object[rowCount][];
for (int row = 0; row < rowCount; row++)
{
    target[row] = new object[columnCount];
    for (int col = 0; col < columnCount; col++)
    {
        var cellValue = Getter(row, col);
        target[row][columnCount] = cellValue;
    }
}

总之,阅读起来很简单:

var columnCount = columns.Length;
var rowCount = columns[0].Length;

object Getter(int row, int col)
{
    bool outOfBounds = (row >= columns[col].Length);
    return outOfBounds ? null : columns[col][row];
}

object[][] target = new object[rowCount][];

for (int row = 0; row < rowCount; row++)
{
    target[row] = new object[columnCount];
    for (int col = 0; col < columnCount; col++)
    {
        var cellValue = Getter(row, col);
        target[row][columnCount] = cellValue;
    }
}

推荐阅读