如何在 Excel 中使用 C# 有效地比较连续行和顺序行?

How can I efficiently compare contiguous and sequential rows using C# in Excel?

我正在用 C# 为 Excel 开发一个 VSTO 插件,它需要比较潜在的大数据集(100 列 x ~10000 或更多行)。它正在 Excel 中完成,因此最终用户可以逐行查看所提供数据的一些图形表示。尽管使用这些大型数据集存在潜在缺陷,但此应用程序必须在 Excel 内完成。

无论如何,我的问题与比较连续行和连续行的有效方法有关。我的目标是将一行与紧随其后的一行进行比较;如果 row1 和 row2 之间的任何元素发生变化,这将计为 "event" 并且 row2 输出到单独的 sheet。我相信您可以看到,当计数约为 10000 时,对于行的逐行比较,这需要很长时间(实际上,对于当前代码,每行大约需要 150 毫秒到 200 毫秒)。

目前我使用SequenceEqual()方法比较两个字符串列表如下:

    private void FilterRawDataForEventReader(Excel.Application xlApp)
    {   
        List<string> row1 = new List<string>();
        List<string> row2 = new List<string>();

        xlWsRaw = xlApp.Worksheets["Full Raw Data"];
        xlWsEventRaw = xlApp.Worksheets["Event Data"];
        Excel.Range xlRawRange = xlWsRaw.Range["A3"].Resize[xlWsRaw.UsedRange.Rows.Count-2, xlWsRaw.UsedRange.Columns.Count];
        var array = xlRawRange.Value;

        Excel.Range xlRange = (Excel.Range)xlWsEventRaw.Cells[xlWsEventRaw.UsedRange.Rows.Count, 1];
        int lastRow = xlRange.get_End(Excel.XlDirection.xlUp).Row;
        int newRow = lastRow + 2;

        for (int i = 1; i < xlWsRaw.UsedRange.Rows.Count - 2; i++)
        {
            row1.Clear();
            row2.Clear();

            for (int j = 1; j <= xlWsRaw.UsedRange.Columns.Count-1; j++)
            {                   
                row1.Add(array[i, j].ToString());
                row2.Add(array[i + 1, j].ToString());
            }
            if (!row1.SequenceEqual(row2))
            {
                row2.Add(array[i + 1, xlWsRaw.UsedRange.Columns.Count].ToString()); // Add timestamp to row2.
                for (int j = 0; j < row2.Count; j++)
                {
                    xlWsEventRaw.Cells[newRow, j + 1] = row2[j];
                }
                newRow++;
            }
        }           
    }

在测试期间,我在该方法的各个部分放置了计时器,以查看某些操作需要多长时间。对于 100 列,为 row1 和 row2 构建字符串数组的第一个循环每次迭代大约需要 100 毫秒,当找到 "event" 时,整个操作需要 150 毫秒到 200 毫秒。

我的直觉是构建两个 List<string> 是问题所在,但根据我的经验,我不知道如何解决此类问题。我要强调的是,两个 List<string> 中数据的实际值无关紧要;重要的是数据是否完全不同。那样的话,我觉得我正在错误地处理这个问题,但不知道如何"re-approach"这么说。

我想知道,如果不是通过迭代构建字符串数组并将它们与 SequenceEqual() 方法进行比较,是否有人可以建议一种更快的方法来比较连续行和顺序行?

如果此解决方案可能对尝试在 C# 中使用 Excel 并进行一些比较的其他人有用:

这个问题主要是一个优化练习。通过消除多个循环并使用 Excel 代替生成比较列表:

    for (int i = 3; i < xlWsRaw.UsedRange.Rows.Count - 2; i++)
    {

        rng1 = (Excel.Range)xlWsRaw.Range[xlWsRaw.Cells[i, 1], xlWsRaw.Cells[i, xlWsRaw.UsedRange.Columns.Count - 1]];
        rng2 = (Excel.Range)xlWsRaw.Range[xlWsRaw.Cells[i+1, 1], xlWsRaw.Cells[i+1, xlWsRaw.UsedRange.Columns.Count - 1]];
        rng3 = (Excel.Range)xlWsEventRaw.Range[xlWsEventRaw.Cells[newRow, 1], xlWsEventRaw.Cells[newRow, xlWsRaw.UsedRange.Columns.Count - 1]];

        object[,] cellValues1 = (object[,])rng1.Value2;
        object[,] cellValues2 = (object[,])rng2.Value2;
        List<string> test1 = cellValues1.Cast<object>().ToList().ConvertAll(x => Convert.ToString(x));
        List<string> test2 = cellValues2.Cast<object>().ToList().ConvertAll(x => Convert.ToString(x));

        if (!test1.SequenceEqual(test2))
        {
            rng2.Copy(rng3);
            xlWsEventRaw.Cells[newRow, xlWsRaw.UsedRange.Columns.Count].Value = xlWsRaw.Cells[i + 1, xlWsRaw.UsedRange.Columns.Count].Value; // Outputs the timestamp of the event to the events worksheet.
            newRow++;
        }
    }

我相信这可以进一步优化,但在我的例子中,范围包含多种类型,包括字符串,所以为了比较,我将所有内容都转换为 List<string>SequenceEqual() 方法,无论它在幕后如何工作,几乎是瞬时的,并将比较 120 列的时间减少到大约 3 毫秒。