Как я могу эффективно сравнивать смежные и последовательные строки, используя C# в Excel? - PullRequest
0 голосов
/ 18 июня 2020

Я разрабатываю надстройку VSTO для Excel в C#, которая должна сравнивать потенциально большие наборы данных (100 столбцов x ~ 10000 или более строк). Это делается в Excel, поэтому конечный пользователь может просматривать графическое представление предоставленных данных построчно. Это приложение должно быть выполнено в Excel, несмотря на потенциальные ловушки использования этих больших наборов данных.

Тем не менее, мой вопрос касается эффективного способа сравнения смежных и последовательных строк. Моя цель - сравнить одну строку со строкой сразу после нее; если есть изменение любого из элементов между строкой1 и строкой2, это считается «событием» и вывод строки2 на отдельный лист. Я уверен, что вы можете видеть, что для построчного сравнения строк, когда счетчик составляет около 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 столбцов первый l oop, который создает массивы строк для row1 и row2, занимает около 100 мс на итерацию, а вся операция занимает от 150 мс до 200 мс, когда "событие" было обнаружено.

Моя интуиция подсказывает, что создание двух List<string> - это проблема, но из своего опыта я не знаю, как еще подойти к подобному типу проблемы. Я должен подчеркнуть, фактические значения данных в двух List<string> не имеют значения; важно то, отличаются ли данные вообще. Таким образом, я чувствую, что подхожу к этой проблеме неправильно, но не знаю, как, так сказать, "подойти заново". их с помощью метода SequenceEqual(), кто-нибудь может предложить более быстрый способ сравнения смежных и последовательных строк?

1 Ответ

0 голосов
/ 19 июня 2020

В случае, если это решение может быть полезно для кого-то еще, пытающегося использовать Excel в C# и провести некоторые сравнения:

Эта проблема была в основном упражнением по оптимизации. Удалив несколько циклов и используя 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 мс.

...