Excel VBA - присвоение массиву изменений LBound и UBound

У меня есть несколько очень больших наборов данных в Excel, которые мне нужно проанализировать, и сделать это в массиве быстрее, чем перебирать данные на листе. Загрузка всех данных в массив вызывает проблемы с памятью (наборы данных ТАКИЕ большие), поэтому я планирую загружать подмножества данных в массив, обрабатывать их, а затем загружать другое подмножество. Я надеялся использовать «функцию» массива для определения LBound и UBound, чтобы помочь мне отслеживать, где я нахожусь на листе. Но я обнаружил, что присвоение значений рабочего листа массиву меняет границы. Следующий код демонстрирует проблему ...

    Sub myTest3()
    Dim myRange As Range
    Dim myArray As Variant
    Dim myOffset As Long

        myOffset = 10
        Set myRange = Worksheets("RawData").Range("A1").CurrentRegion
        ReDim myArray(myOffset To myRange.Rows.Count, myRange.Columns.Count)
        MsgBox LBound(myArray, 1) & " to " & UBound(myArray)

        Set myRange = myRange.Offset(myOffset, 0).Resize(myRange.Rows.Count - myOffset, myRange.Columns.Count)

        myArray = myRange.Value2

        MsgBox LBound(myArray, 1) & " to " & UBound(myArray)

    End Sub

Первый MsgBox дает мне «от 10 до 10931». Второй MsgBox дает мне «от 1 до 10921».

Есть идеи по поддержанию границ массива, как я их изначально определил? Я знаю, что перебор рабочего листа для выполнения задания сработает, но это будет медленным.

Заранее спасибо.


person OldUgly    schedule 20.10.2013    source источник
comment
Назначение массива из диапазона с использованием .Value всегда создает массив, начинающийся с 1. Если вам нужно другое поведение, вам нужно будет заполнить массив с помощью цикла.   -  person Tim Williams    schedule 21.10.2013
comment
Раскрась меня грустно. Спасибо, Тим. Думаю, мне нужно будет реструктурировать свой код, чтобы я мог отслеживать свое местоположение с помощью цикла.   -  person OldUgly    schedule 21.10.2013
comment
Проблема в том, что вы используете оператор ReDim для повторного объявления массива с нижней границей 10, здесь: ReDim myArray(myOffset to .... Что именно вы пытаетесь сделать с переменной myOffset? Думаю, в этом причина.   -  person David Zemens    schedule 21.10.2013
comment
На самом деле, Дэвид, это особенность, которой я пытаюсь воспользоваться. В VBA вы можете установить для LBound все, что захотите. В моем проекте (не обязательно в примере кода) я надеялся назначить нижнюю и верхнюю границы массива, чтобы они соответствовали строкам на листе, из которого были взяты данные. Это позволило бы мне вернуть его в те же места после соответствующих изменений. Я думаю, что комментарий Тима - это ... при назначении массива из диапазона он, кажется, автоматически перенаправляет массив с LBound = 1.   -  person OldUgly    schedule 21.10.2013
comment
@OldUgly Я думаю, _1 _ может вам помочь.   -  person    schedule 21.10.2013


Ответы (1)


Excel VBA в этой ситуации работает не так, как вы хотите. При выполнении myArray = myRange.Value2 исходное содержимое myArray было заменено. Массив Redimmed выбросили. Excel / VBA не смотрит на цель, он заменяет ее или, что более правильно, создает новый массив и заставляет переменную myaArray указывать на него.

Так что вам понадобится немного больше кода, чтобы добраться туда, где вы хотите быть. Я бы подумал о том, чтобы поместить код для захвата следующего фрагмента в отдельную функцию и провести там бухгалтерский учет:

Function ChunkAtOffset(rng As Range, rowsInChunk As Long, colsInChunk As Long, offsetRows As Long) As Variant
' Note: doesn't cater for the case where there are fewer than 'offsetRows' in the target    
Dim arr As Variant, result As Variant
Dim r As Long, c As Long

    arr = rng.offset(offsetRows).Resize(rowsInChunk, colsInChunk).Value2

    ReDim result(offsetRows To offsetRows + rowsInChunk - 1, 1 To colsInChunk)

    For r = 1 To rowsInChunk
        For c = 1 To colsInChunk
            result(offsetRows - 1 + r, c) = arr(r, c)
        Next
    Next

    ChunkAtOffset = result

End Function

Если я запустил это:

Sub myTest4()

    Dim curReg As Range, ary As Variant, offset As Long
    With Range("A1")
        Set curReg = .CurrentRegion
        Do
            ary = ChunkAtOffset(.CurrentRegion, 10, .CurrentRegion.Columns.Count, offset)
            Debug.Print LBound(ary, 1) & " to " & UBound(ary)
            offset = offset + 10
        Loop Until offset >= .CurrentRegion.Rows.Count
    End With

End Sub

... Теперь я понял:

0 to 9
10 to 19
20 to 29
person Mike Woodhouse    schedule 21.10.2013
comment
это выглядит хорошо. В итоге я сделал нечто подобное. Всем спасибо за помощь. - person OldUgly; 22.10.2013