Используйте findnext для заполнения многомерного массива VBA Excel

Мой вопрос на самом деле касается вопроса, который распространяется на EXCEL VBA Хранить результаты поиска в массиве?

Здесь Андреас попытался выполнить поиск по столбцу и сохранить совпадения в массиве. Я пробую то же самое. Но отличается тем, что при (1) нахождении значения (2) я хочу скопировать разные типы значений из (3) ячеек в той же строке, где было найдено искомое значение (4), в двумерный массив.

Таким образом, массив (концептуально) будет выглядеть примерно так:

Searchresult.1st SameRow.Cell1.Value1 SameRow.Cell2.Value2 SameRow.Cell3.Value3
Searchresult.2nd SameRow.Cell1.Value1 SameRow.Cell2.Value2 SameRow.Cell3.Value3
Searchresult.3rd SameRow.Cell1.Value1 SameRow.Cell2.Value2 SameRow.Cell3.Value3

Etc.

Код, который я использую, выглядит так:

Sub fillArray()

Dim i As Integer
Dim aCell, bCell As Range
Dim arr As Variant

i = 0 

Set aCell = Sheets("Log").UsedRange.Find(What:=("string"), _
                    LookIn:=xlValues, _
                    LookAt:=xlWhole, _
                    SearchOrder:=xlByRows, _
                    SearchDirection:=xlNext, _
                    MatchCase:=False, _
                    SearchFormat:=False)

If Not aCell Is Nothing Then
    Set bCell = aCell
    ReDim Preserve arr(i, 5)
    arr(i, 0) = True 'Boolean
    arr(i, 1) = aCell.Value 'String
    arr(i, 2) = aCell.Cells.Offset(0, 1).Value 
    arr(i, 3) = aCell.Cells.Offset(0, 3).Value
    arr(i, 4) = aCell.Cells.Offset(0, 4).Value
    arr(i, 5) = Year(aCell.Cells.Offset(0, 3).Value)

    i = i + 1

    Do While exitLoop = False
            Set aCell = Sheets("Log").UsedRange.FindNext(after:=aCell)

            If Not aCell Is Nothing Then
                If aCell.Address = bCell.Address Then Exit Do
                'ReDim Preserve arrSwUb(i, 5)
                    arr(i, 0) = True
                    arr(i, 1) = aCell.Value
                    arr(i, 2) = aCell.Cells.Offset(0, 1).Value
                    arr(i, 3) = aCell.Cells.Offset(0, 3).Value
                    arr(i, 4) = aCell.Cells.Offset(0, 4).Value
                    arr(i, 5) = Year(aCell.Cells.Offset(0, 3).Value)

                    i = i + 1
            Else
                exitLoop = True
            End If
    Loop


End If

End Sub

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

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


person Evert Van Steen    schedule 15.08.2012    source источник


Ответы (3)


ReDim Preserve может изменять размер только последнего измерения вашего массива: http://msdn.microsoft.com/en-us/library/w8k3cys2(v=vs.71).aspx

Из приведенной выше ссылки:

Сохранить

Optional. Keyword used to preserve the data in the existing array when you change the size of only the last dimension.

Изменить: Это не очень полезно, не так ли. Предлагаю вам транспонировать ваш массив. Кроме того, сообщения об ошибках от функций массива УЖАСНЫЕ.

По предложению Сиддарта попробуйте это. Сообщите мне, если у вас возникнут проблемы:

Sub fillArray()
    Dim i As Integer
    Dim aCell As Range, bCell As Range
    Dim arr As Variant

    i = 0
    Set aCell = Sheets("Log").UsedRange.Find(What:=("string"), _
                                             LookIn:=xlValues, _
                                             LookAt:=xlWhole, _
                                             SearchOrder:=xlByRows, _
                                             SearchDirection:=xlNext, _
                                             MatchCase:=False, _
                                             SearchFormat:=False)
    If Not aCell Is Nothing Then
        Set bCell = aCell
        ReDim Preserve arr(0 To 5, 0 To i)
        arr(0, i) = True 'Boolean
        arr(1, i) = aCell.Value 'String
        arr(2, i) = aCell.Cells.Offset(0, 1).Value
        arr(3, i) = aCell.Cells.Offset(0, 3).Value
        arr(4, i) = aCell.Cells.Offset(0, 4).Value
        arr(5, i) = Year(aCell.Cells.Offset(0, 3).Value)
        i = i + 1
        Do While exitLoop = False
            Set aCell = Sheets("Log").UsedRange.FindNext(after:=aCell)
            If Not aCell Is Nothing Then
                If aCell.Address = bCell.Address Then Exit Do
                ReDim Preserve arrSwUb(0 To 5, 0 To i)
                arr(0, i) = True
                arr(1, i) = aCell.Value
                arr(2, i) = aCell.Cells.Offset(0, 1).Value
                arr(3, i) = aCell.Cells.Offset(0, 3).Value
                arr(4, i) = aCell.Cells.Offset(0, 4).Value
                arr(5, i) = Year(aCell.Cells.Offset(0, 3).Value)
                i = i + 1
            Else
                exitLoop = True
            End If
        Loop
    End If
End Sub

Примечание: в объявлениях у вас было:

Dim aCell, bCell as Range

Это то же самое, что:

Dim aCell as Variant, bCell as Range

Некоторый тестовый код для демонстрации вышеизложенного:

Sub testTypes()

    Dim a, b As Integer
    Debug.Print VarType(a)
    Debug.Print VarType(b)

End Sub
person mkingston    schedule 15.08.2012
comment
+1 за лишнюю милю;) - person Siddharth Rout; 16.08.2012
comment
Ааа, да, вот как это работает (и это действительно работает :)! Я знал, что был близок, но просто не мог туда добраться. Из-за этого я 2 дня ломал себе голову. Я искренне благодарен за вашу помощь. Кстати, вы уверены, что димминг работает именно так? Я всегда думал, что при объявлении разделение переменных запятой делает их всех одного типа: msdn.microsoft.com/en-us/library/x397t1yt%28v=vs.71%29.aspx - person Evert Van Steen; 16.08.2012
comment
@EvertVanSteen рада помочь. Я думаю, что в VB (а не в VBA) это работает так. См. Мою правку отрывка примера кода, который покажет вам, что типы переменных различаются. - person mkingston; 16.08.2012
comment
Еще одна вещь: вам понадобится немедленное окно, чтобы отобразить вывод оператора debug.print. Нажмите ctrl + g, чтобы отобразить это. - person mkingston; 16.08.2012

Вот вариант, который предполагает, что вы можете измерить массив в начале. Я использовал WorsheetFunction.Countif в UsedRange для «строки», которая, похоже, должна работать:

Option Explicit

    Sub fillArray()

    Dim i As Long
    Dim aCell As Range, bCell As Range
    Dim arr() As Variant
    Dim SheetToSearch As Excel.Worksheet
    Dim StringCount As Long

    Set SheetToSearch = ThisWorkbook.Worksheets("log")
    i = 1

    With SheetToSearch
        StringCount = Application.WorksheetFunction.CountIf(.Cells, "string")
        ReDim Preserve arr(1 To StringCount, 1 To 6)
        Set aCell = .UsedRange.Find(What:=("string"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)

        If Not aCell Is Nothing Then
            arr(i, 1) = True    'Boolean
            arr(i, 2) = aCell.Value    'String
            arr(i, 3) = aCell.Cells.Offset(0, 1).Value
            arr(i, 4) = aCell.Cells.Offset(0, 3).Value
            arr(i, 5) = aCell.Cells.Offset(0, 4).Value
            arr(i, 6) = Year(aCell.Cells.Offset(0, 3).Value)
            Set bCell = aCell
            i = i + 1

            Do Until i > StringCount
                Set bCell = .UsedRange.FindNext(after:=bCell)
                If Not bCell Is Nothing Then
                    arr(i, 1) = True    'Boolean
                    arr(i, 2) = bCell.Value    'String
                    arr(i, 3) = bCell.Cells.Offset(0, 1).Value
                    arr(i, 4) = bCell.Cells.Offset(0, 3).Value
                    arr(i, 5) = bCell.Cells.Offset(0, 4).Value
                    arr(i, 6) = Year(bCell.Cells.Offset(0, 3).Value)
                    i = i + 1
                End If
            Loop
        End If
    End With

    End Sub

Обратите внимание, что я исправил некоторые проблемы в ваших объявлениях. Я добавил Option Explicit, который заставляет вас объявлять свои переменные - exitLoop не был объявлен. Теперь и aCell, и bCell являются диапазонами - раньше был только bCell (прокрутите вниз до «Pay Внимание к переменным заявлено одним тусклым заявлением »). Я также создал переменную рабочего листа и заключил ее в оператор With. Кроме того, я начал оба измерения массива с 1, потому что ... ну, потому что я хотел, наверное :). Я также упростил логику выхода из цикла - не думаю, что вам все это нужно, чтобы сказать, когда выходить.

person Doug Glancy    schedule 15.08.2012
comment
Да! Пробовал тоже. Работает отлично :) Большое спасибо. - person Evert Van Steen; 16.08.2012

Вы не можете Redim Preserve создать такой многомерный массив. В многомерном массиве вы можете изменить только последнее измерение при использовании Preserve. Если вы попытаетесь изменить любое из других измерений, произойдет ошибка времени выполнения. Я бы рекомендовал прочитать эту ссылку msdn

сказав, что я могу придумать 2 варианта

Вариант 1

Сохраните результаты в новом временном листе

Вариант 2

Объявите одномерный массив, а затем объедините результаты, используя уникальный разделитель, например "#Evert_Van_Steen#"

Вверху кода

Const Delim As String = "#Evert_Van_Steen#"

Тогда используйте это так

ReDim Preserve arr(i)

arr(i) = True & Delim & aCell.Value & Delim & aCell.Cells.Offset(0, 1).Value & Delim & _
aCell.Cells.Offset(0, 3).Value & Delim & aCell.Cells.Offset(0, 4).Value & Delim & _
Year(aCell.Cells.Offset(0, 3).Value)
person Siddharth Rout    schedule 15.08.2012
comment
Похоже, что OP в настоящее время имеет фиксированное второе измерение, он может просто транспонировать свой массив, поэтому он перекраивает второе измерение. - person mkingston; 16.08.2012
comment
Да, он может это сделать, но для новичка это действительно может быть проблемой. - person Siddharth Rout; 16.08.2012
comment
Честно говоря, я просто поместил этот комментарий на тот случай, если он только прочитает ваш ответ и сочтет его правильным (что ему и следовало бы) - но отдайте ему должное! Он зашел так далеко, я уверен, что он справится, и если вы читаете этот ОП, вы можете спросить :). - person mkingston; 16.08.2012
comment
Поскольку вы упомянули это первым, я бы порекомендовал добавить в свой пост пример того, как это сделать :) - person Siddharth Rout; 16.08.2012
comment
Я положил свои деньги туда, где был мой рот, и Дуг меня избил! Ха-ха! - person mkingston; 16.08.2012
comment
Так ли очевидно, что я новичок? Учитывая, что я начал кодировать только через пару недель, я думаю, что добился большого прогресса :) И ваши комментарии здесь очень помогли мне понять массивы. Кажется логичным, теперь вы можете изменить только последнее измерение. Просто никогда не переставал думать об этом. Я не уверен, что хочу транспонировать массив. Поскольку он будет перевернут в отношении источника, я боюсь, что это повредит моему мозгу при использовании массива дальше по строке;) Если нет особых причин, это на самом деле лучший вариант? - person Evert Van Steen; 16.08.2012
comment
Второй вариант, который я предложил, гораздо проще понять по сравнению с транспонированием массивов. Но если вы понимаете, как транспонировать массив, то ничего подобного;) - person Siddharth Rout; 16.08.2012
comment
Ой, подождите, вы правы. Мне нужно это переставить. Я не видел этого, потому что случайно использовал транспонирующий вид массива. Спасибо. - person Evert Van Steen; 16.08.2012