Подтвердить что ты не робот

Excel VBA - Как переделать 2D-массив?

В Excel через Visual Basic я повторяю через CSV файл счетов-фактур, загружаемых в Excel. Счета-фактуры находятся в определяемом шаблоне клиентом.

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

Где-то у меня синтаксис неправильный. Он продолжает говорить мне, что я уже Dimensionalized массив. Как-то я создал его как статический массив? Что мне нужно исправить, чтобы позволить ему работать динамически?

РАБОЧИЙ КОД НА ОТВЕТ ДАННЫХ

Sub InvoicesUpdate()
'
'Application Settings
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual

'Instantiate control variables
Dim allRows As Long, currentOffset As Long, invoiceActive As Boolean, mAllRows As Long
Dim iAllRows As Long, unusedRow As Long, row As Long, mWSExists As Boolean, newmAllRows As Long

'Instantiate invoice variables
Dim accountNum As String, custName As String, vinNum As String, caseNum As String, statusField As String
Dim invDate As String, makeField As String, feeDesc As String, amountField As String, invNum As String

'Instantiate Workbook variables
Dim mWB As Workbook 'master
Dim iWB As Workbook 'import

'Instantiate Worksheet variables
Dim mWS As Worksheet
Dim iWS As Worksheet

'Instantiate Range variables
Dim iData As Range

'Initialize variables
invoiceActive = False
row = 0

'Open import workbook
Workbooks.Open ("path:excel_invoices.csv")
Set iWB = ActiveWorkbook
Set iWS = iWB.Sheets("excel_invoices.csv")
iWS.Activate
Range("A1").Select
iAllRows = iWS.UsedRange.Rows.Count 'Count rows of import data

'Instantiate array, include extra column for client name
Dim invoices()
ReDim invoices(10, 0) 

'Loop through rows.
Do

    'Check for the start of a client and store client name
    If ActiveCell.Value = "Account Number" Then

        clientName = ActiveCell.Offset(-1, 6).Value

    End If

    If ActiveCell.Offset(0, 3).Value <> Empty And ActiveCell.Value <> "Account Number" And ActiveCell.Offset(2, 0) = Empty Then

        invoiceActive = True

        'Populate account information.
        accountNum = ActiveCell.Offset(0, 0).Value
        vinNum = ActiveCell.Offset(0, 1).Value
        'leave out customer name for FDCPA reasons
        caseNum = ActiveCell.Offset(0, 3).Value
        statusField = ActiveCell.Offset(0, 4).Value
        invDate = ActiveCell.Offset(0, 5).Value
        makeField = ActiveCell.Offset(0, 6).Value

    End If

    If invoiceActive = True And ActiveCell.Value = Empty And ActiveCell.Offset(0, 6).Value = Empty And ActiveCell.Offset(0, 9).Value = Empty Then

        'Make sure something other than $0 was invoiced
        If ActiveCell.Offset(0, 8).Value <> 0 Then

            'Populate individual item values.
            feeDesc = ActiveCell.Offset(0, 7).Value
            amountField = ActiveCell.Offset(0, 8).Value
            invNum = ActiveCell.Offset(0, 10).Value

            'Transfer data to array
            invoices(0, row) = "=TODAY()"
            invoices(1, row) = accountNum
            invoices(2, row) = clientName
            invoices(3, row) = vinNum
            invoices(4, row) = caseNum
            invoices(5, row) = statusField
            invoices(6, row) = invDate
            invoices(7, row) = makeField
            invoices(8, row) = feeDesc
            invoices(9, row) = amountField
            invoices(10, row) = invNum

            'Increment row counter for array
            row = row + 1

            'Resize array for next entry
            ReDim Preserve invoices(10,row)

         End If

    End If

    'Find the end of an invoice
    If invoiceActive = True And ActiveCell.Offset(0, 9) <> Empty Then

        'Set the flag to outside of an invoice
        invoiceActive = False

    End If

    'Increment active cell to next cell down
    ActiveCell.Offset(1, 0).Activate

'Define end of the loop at the last used row
Loop Until ActiveCell.row = iAllRows

'Close import data file
iWB.Close
4b9b3361

Ответ 1

Это не совсем интуитивно понятно, но вы не можете переделать (VB6 Ref) массив, если вы затушевали его с размерами. Точная цитата из связанной страницы:

Оператор ReDim используется для изменения размера или изменения размера динамического массива, который имеет уже было официально объявлено с использованием Private, Public или Dim с пустыми скобками (без индексных индексов).

Другими словами, вместо dim invoices(10,0)

Вы должны использовать

Dim invoices()
Redim invoices(10,0)

Затем, когда вы ReDim, вам нужно будет использовать Redim Preserve (10,row)

Предупреждение. При перенастройке многомерных массивов, если вы хотите сохранить свои значения, вы можете увеличить только последнее измерение. И.Е. Redim Preserve (11,row) или даже (11,0) завершится с ошибкой.

Ответ 2

Я наткнулся на этот вопрос, когда сам ударил этот дорожный блок. В итоге я написал кусок кода, который бы быстро справился с этим ReDim Preserve в массиве нового размера (первое или последнее измерение). Возможно, это поможет другим, кто сталкивается с той же проблемой.

Итак, для использования предположим, что ваш массив изначально установлен как MyArray(3,5), и вы хотите увеличить размеры (сначала слишком!), просто скажите MyArray(10,20). Вы привыкли бы делать что-то вроде этого?

 ReDim Preserve MyArray(10,20) '<-- Returns Error

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

 MyArray = ReDimPreserve(MyArray,10,20)

Теперь массив больше, и данные сохраняются. Ваш ReDim Preserve для массива Multi-Dimension завершен.:)

И последнее, но не менее важное: чудесная функция: ReDimPreserve()

'redim preserve both dimensions for a multidimension array *ONLY
Public Function ReDimPreserve(aArrayToPreserve,nNewFirstUBound,nNewLastUBound)
    ReDimPreserve = False
    'check if its in array first
    If IsArray(aArrayToPreserve) Then       
        'create new array
        ReDim aPreservedArray(nNewFirstUBound,nNewLastUBound)
        'get old lBound/uBound
        nOldFirstUBound = uBound(aArrayToPreserve,1)
        nOldLastUBound = uBound(aArrayToPreserve,2)         
        'loop through first
        For nFirst = lBound(aArrayToPreserve,1) to nNewFirstUBound
            For nLast = lBound(aArrayToPreserve,2) to nNewLastUBound
                'if its in range, then append to new array the same way
                If nOldFirstUBound >= nFirst And nOldLastUBound >= nLast Then
                    aPreservedArray(nFirst,nLast) = aArrayToPreserve(nFirst,nLast)
                End If
            Next
        Next            
        'return the array redimmed
        If IsArray(aPreservedArray) Then ReDimPreserve = aPreservedArray
    End If
End Function

Я написал это примерно через 20 минут, поэтому никаких гарантий нет. Но если вы хотите использовать или расширять его, не стесняйтесь. Я бы подумал, что у кого-то уже был бы такой код здесь, но, видимо, нет. Итак, здесь идут разные редукторы.

Ответ 3

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

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

индексы будут, чем 00-01, 01-11, 02-12, 03-13, 04-14, 05-15... 0 25-1 25 и т.д. вместо 00-01, 10-11, 20-21, 30-31, 40-41 и т.д.

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

Ответ 4

здесь обновлен код метода redim preseve с объявлением variabel, надеюсь, что @Control Freak в порядке с ним:)

Option explicit
'redim preserve both dimensions for a multidimension array *ONLY
Public Function ReDimPreserve(aArrayToPreserve As Variant, nNewFirstUBound As Variant, nNewLastUBound As Variant) As Variant
    Dim nFirst As Long
    Dim nLast As Long
    Dim nOldFirstUBound As Long
    Dim nOldLastUBound As Long

    ReDimPreserve = False
    'check if its in array first
    If IsArray(aArrayToPreserve) Then
        'create new array
        ReDim aPreservedArray(nNewFirstUBound, nNewLastUBound)
        'get old lBound/uBound
        nOldFirstUBound = UBound(aArrayToPreserve, 1)
        nOldLastUBound = UBound(aArrayToPreserve, 2)
        'loop through first
        For nFirst = LBound(aArrayToPreserve, 1) To nNewFirstUBound
            For nLast = LBound(aArrayToPreserve, 2) To nNewLastUBound
                'if its in range, then append to new array the same way
                If nOldFirstUBound >= nFirst And nOldLastUBound >= nLast Then
                    aPreservedArray(nFirst, nLast) = aArrayToPreserve(nFirst, nLast)
                End If
            Next
        Next
        'return the array redimmed
        If IsArray(aPreservedArray) Then ReDimPreserve = aPreservedArray
    End If
End Function

Ответ 5

Вот как я это делаю.

Dim TAV() As Variant
Dim ArrayToPreserve() as Variant

TAV = ArrayToPreserve
ReDim ArrayToPreserve(nDim1, nDim2)
For i = 0 To UBound(TAV, 1)
    For j = 0 To UBound(TAV, 2)
        ArrayToPreserve(i, j) = TAV(i, j)
    Next j
Next i

Ответ 6

Небольшое обновление того, что @control freak и @skatun писали ранее (извините, у меня недостаточно репутации, чтобы просто комментировать). Я использовал код skatun, и он работал хорошо для меня, за исключением того, что он создавал массив большего размера, чем мне было нужно. Поэтому я изменил:

ReDim aPreservedArray(nNewFirstUBound, nNewLastUBound)

чтобы:

ReDim aPreservedArray(LBound(aArrayToPreserve, 1) To nNewFirstUBound, LBound(aArrayToPreserve, 2) To nNewLastUBound)

Это сохранит все нижние границы исходного массива (либо 0, 1, либо что угодно; исходный код предполагает 0) для обоих измерений.

Ответ 7

я решил это более коротким способом.

Dim marray() as variant, array2() as variant, YY ,ZZ as integer
YY=1
ZZ=1

Redim marray(1 to 1000, 1 to 10)
Do while ZZ<100 ' this is populating the first array
marray(ZZ,YY)= "something"
ZZ=ZZ+1
YY=YY+1 
Loop
'this part is where you store your array in another then resize and restore to original
array2= marray
Redim marray(1 to ZZ-1, 1 to YY)
marray = array2

Ответ 8

Вот и ты.

Открытая функция ReDimPreserve (ByRef Arr, ByVal idx1 как целое число, ByVal idx2 как целое число)

Dim newArr()
Dim x As Integer
Dim y As Integer

ReDim newArr(idx1, idx2)

For x = 0 To UBound(Arr, 1)
    For y = 0 To UBound(Arr, 2)
        newArr(x, y) = Arr(x, y)
    Next
Next

Arr = newArr

End Function