Как я могу "повторно сохранить" двумерный массив в Excel 2007 VBA, чтобы добавить в него строки, а не столбцы?
Я работаю с динамическим массивом в Excel VBA. Количество столбцов (m) фиксировано, однако я не знаю, сколько строк (n) потребуется.
В справочных документах указано, что ReDim Preserve myArray (n, m) позволяет мне сделать m больше, но не n. однако мне нужно увеличить число строк (n), сохраняя мои данные, а не столбцы (m)!
Например, у меня может быть массив (5,20), который я хотел бы расширить до (10,20), сохраняя мои данные.
Похоже, что если бы был какой-то способ транспонировать мой массив, сделать ReDim сохранить, чтобы расширить число "столбцов", а затем повторно транспонировать мой массив, я мог бы выполнить то, что я хочу.
Правильно ли это делать? Если да, то как я могу это сделать?
Есть ли лучший способ добиться того, чего я хочу?
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 SubTempValue считывается из ячеек 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