Как я могу "повторно сохранить" двумерный массив в Excel 2007 VBA, чтобы добавить в него строки, а не столбцы?



Я работаю с динамическим массивом в Excel VBA. Количество столбцов (m) фиксировано, однако я не знаю, сколько строк (n) потребуется.



В справочных документах указано, что ReDim Preserve myArray (n, m) позволяет мне сделать m больше, но не n. однако мне нужно увеличить число строк (n), сохраняя мои данные, а не столбцы (m)!



Например, у меня может быть массив (5,20), который я хотел бы расширить до (10,20), сохраняя мои данные.



Похоже, что если бы был какой-то способ транспонировать мой массив, сделать ReDim сохранить, чтобы расширить число "столбцов", а затем повторно транспонировать мой массив, я мог бы выполнить то, что я хочу.



Правильно ли это делать? Если да, то как я могу это сделать?



Есть ли лучший способ добиться того, чего я хочу?
537   8  

8 ответов:

Один из способов сделать то, что вы хотите,-это использовать 1-D массив, содержащий 1-d массивы вместо 2-D массива. Затем вы можете повторно сохранить внешний массив, сколько захотите. Если вы возвращаете внешний массив из функции, Excel сделает все правильно и принудит его к 2-D массиву.

Например, функция ниже возвращает массив 3x2 в ячейки, из которых он вызывается:

Public Function nested()
    Dim outer
    outer = Array(Array(1, 2), Array(3, 4))

    ReDim Preserve outer(1 To 3)

    outer(3) = Array(5, 6)

    nested = outer
End Function

Мой ответ на эти вопросы также может быть вам полезен: передайте многомерный массив в Excel UDF в VBA и VBA вставка 3-мерного массива в лист

Конечно, если вы не возвращаете это из UDF, вам придется принудить его самостоятельно. Простой способ сделать это без написания циклического кода состоит в следующем:

Dim coerced
coerced = Application.Index(outer, 0, 0)

Это просто вызов встроенной индексной функции Excel, и нули означают, что вы хотите вернуть все свои строки и все свои столбцы. Excel автоматически принудит ваш 1-D массив 1-d массивов к 2-D массиву. (Предостережение: есть некоторые размеры ограничения, но они намного больше, чем 10x20.)

Один из способов, как вы могли бы засеять его, действительно, двойной транспозицией с изменением числа столбцов между ними. Однако это будет работать только для двумерных массивов. Это делается следующим образом:

' Adding one row is done by a double transposing and adding a column in between.
' (Excel VBA does not allow to change the size of the non-last dimension of a
' multidimensional array.)
myArray = Application.Transpose(myArray)
ReDim Preserve myArray(1 To m, 1 To n + 1)
myArray= Application.Transpose(myArray)

Конечно m и n можно вывести следующим образом:

m = UBound(myArray, 1)
n = UBound(myArray, 2)

Таким образом, вы используете встроенную функцию транспонирования самого Excel. Как уже упоминалось в комментариях к коду, это не будет работать для матриц более высокого порядка.

Если вы разработчик-в чем разница между строками и столбцами? Использование array (N, 2) (Если у вас есть 2 столбца) совпадает с array (2, N) - для которого вы можете

ReDim Preserve arr(1 to 2, 1 to N+1). 

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

N = ubound(arr)
FOR i=1 to N
    GetColumn1Value = arr(1, i)
    GetColumn2Value = arr(2, i)
NEXT i

Или вы хотите этого:

N = ubound(arr)
FOR i=1 to N
    GetColumn1Value = arr(i, 1)
    GetColumn2Value = arr(i, 2)
NEXT i

В чем разница?

Тут же на ум приходит слово "транспонировать". Вы можете просто ввести данные в двумерный массив, перевернув столбцы и строки (т. е. транспонировать), эффективно позволяя вам сделать n (теперь количество столбцов, но хранение значений строк) больше, когда вам нужно.

Чтобы ссылаться на значения, скажем, в двойном цикле, поменяйте индексы местами. Например, вместо того, чтобы перейти от i = 1 к n и j = 1 к m, где вы ссылаетесь на значение (i, j), используйте i = 1 к m и j = 1 к n.

Нет способа определить количество элементов в первом измерении? Лентяй. Для двумерного массива с фиксированным вторым измерением, возможно, вы захотите сделать его массивом типов ("структур" в других языках) вместо этого. Это позволит вам использовать Redim Preserve,и все еще оставляет вас с разумным способом добавления и доступа к значениям, хотя теперь вы будете получать доступ ко второму измерению как именованные члены типа, а не как значения Индекса.

Решил свой собственный вопрос; вот как я обошел свою проблему. Я создал временный массив, скопировал содержимое myArray во временный массив, изменил размер myArray, а затем скопировал содержимое обратно из временного массива в myArray.

tempArray = myArray
ReDim myArray(1 To (UBound(myArray()) * 2), 1 To m)
For i = 1 To n
     For j = 1 To m
          myArray(i, j) = tempArray(i, j)
     Next j
Next i

Если кто-нибудь может предложить более эффективный способ сделать это, я хотел бы услышать его.

Принудительное или нарезание, похоже, не работает с индексом (или совпадением (Index (когда я хочу фильтровать массив (w / o циклы) на основе нескольких критериев, когда размер данных охватывает более 2^16 строк (~ 92000 строк).

Run-Time error '13':

Type Mismatch

Транспонирование не работает с большими наборами записей и поэтому также не работает двойная транспонирование. разве нет способа фильтровать массив и захватывать данные, не прибегая к нескольким циклам?

Я думаю попробовать словарный путь или ADO с Excel.

Массив с 2 измерениями, где число столбцов фиксировано, а число строк динамично, можно создать следующим образом:

Sub test2DimArray()
Dim Arr2D() As String
Dim NumberOfCol As Long
Dim I As Long, J As Long, x As Long
Dim tmpValue As String, tmpValue2 As String, tmpValue3 As String

NumberOfCol = 3
J = 1
Debug.Print "Run " & Now()
Debug.Print "Sheet content"
Debug.Print "Row   col1     col2     col3"

For I = 1 To 10
tmpValue = Cells(I, 1).Value
tmpValue2 = Cells(I, 2).Value
tmpValue3 = Cells(I, 3).Value
Debug.Print I & " =    " & tmpValue & "     " & tmpValue2 & "     " & tmpValue3
    If Len(tmpValue) > 0 Then
        ReDim Preserve Arr2D(NumberOfCol, 1 To J)
        Arr2D(1, J) = tmpValue
        Arr2D(2, J) = tmpValue2
        Arr2D(3, J) = tmpValue3
        J = J + 1
    End If
Next

'check array values
Debug.Print vbLf; "arr2d content"
Debug.Print "Row   col1     col2     col3"

For x = LBound(Arr2D, 2) To UBound(Arr2D, 2)
Debug.Print x & " =   " & Arr2D(1, x) & "        " & Arr2D(2, x) & "        " & Arr2D(3, x)
Next

Debug.Print "========================="
End Sub

TempValue считывается из ячеек A1:A10, если в ячейке Ax есть значение, оно переопределяет массив с +1 и добавляет Tempvalue к массиву col1, добавляет содержимое в Bx к массиву col2 и содержимое в Cx к массиву col3. Если длина Ax-значения равна 0, то это ничего не добавляет к массиву.

Отладка.вывод результатов в Редакторе VB в режиме "немедленное окно".

Без тестовых строк и добавления динамического диапазона данных код может быть:

Sub my2DimArray()
Dim Arr2D() As String
Dim NumberOfCol As Long, NumberOfRow As Long
Dim FirstCol As Long, FirstRow As Long, LastCol As Long, LastRow As Long
Dim I As Long, J As Long, X As Long
Dim tmpValue As String, tmpValue2 As String, tmpValue3 As String

'if cells with values start in A1
With ActiveSheet.UsedRange
    NumberOfCol = .Columns.Count
    NumberOfRow = .Rows.Count
End With

'if cells with values starts elsewhere
With ActiveSheet.UsedRange
    FirstCol = .Column
    FirstRow = .Row
    LastCol = .Column + .Columns.Count - 1
    LastRow = .Row + .Rows.Count - 1
End With

J = 1

For I = 1 To NumberOfRow 'or For I = FirstRow to LastRow
tmpValue = Cells(I, 1).Value 'or tmpValue = Cells(I, FirstCol).Value
    If Len(tmpValue) > 0 Then
        ReDim Preserve Arr2D(NumberOfCol, 1 To J)
            For X = 1 To NumberOfCol 'or For X = FirstCol to LastCol
                Arr2D(X, J) = Cells(I, X).Value
            Next X
        J = J + 1
    End If
Next I

End Sub

Comments

    Ничего не найдено.