2010-10-21 2 views
8

Sto lavorando con un array dinamico in VBA di Excel. Il numero di colonne (m) è fisso, tuttavia, non so quante righe (n) saranno richieste.Come è possibile "ReDim Preserve" una matrice 2D in VBA di Excel 2007 in modo che sia possibile aggiungere righe e non colonne all'array?

I documenti della guida indicano che ReDim Preserve myArray (n, m) mi consente di ingrandire m ma non n. Tuttavia, ho bisogno di aumentare il numero di righe (n) preservando i miei dati, non le colonne (m)!

Ad esempio, potrei avere una matrice (5,20) che vorrei espandere a (10,20) preservando i miei dati.

Sembra che se ci fosse un modo per trasporre il mio array, fare un ReDim Preserve per espandere il numero di "colonne", quindi ri-trasporre il mio array, potrei realizzare ciò che voglio.

È questo il modo corretto per farlo? Se è così, come posso farlo?

C'è un modo migliore per realizzare ciò che voglio?

risposta

0

Risolto il mio stesso problema; ecco come ho risolto il mio problema Ho creato un array temporaneo, copiato il contenuto di myArray nell'array temporaneo, ridimensionato myArray, quindi copiato il contenuto dall'array temp a 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 

Se qualcuno può suggerire un modo più efficiente per farlo, mi piacerebbe sentirlo.

+2

-1: Due cicli nidificati per ogni ReDim di un array - non una brillante idea a tutti! Cosa succederà se si dispone di una serie di 10.000 elementi? –

0

Non è possibile determinare il numero di elementi nella prima dimensione? Bummer. Per un array bidimensionale con una seconda dimensione fissa, si potrebbe prendere in considerazione la possibilità di renderlo un array di Tipi ("structs" in altre lingue). Ciò ti consentirà di utilizzare Redim Preserve e ti lascerà comunque un modo ragionevole per aggiungere e accedere ai valori, anche se ora accederai alla seconda dimensione come membri nominati del tipo anziché a valori di indice.

12

Un modo per fare ciò che si desidera è utilizzare un array 1-D che contiene array 1-D anziché un array 2-D. Quindi puoi ReDim Conservare l'array esterno tutto ciò che desideri. Se stai restituendo l'array esterno da una funzione, Excel farà la cosa giusta e la costringerà a un array 2-D.

Ad esempio, la funzione di seguito restituirà una matrice 3x2 per le cellule da essere chiamato da:

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 

La mia risposta a queste domande potrebbe anche essere utile a voi: Pass multidimensional array into Excel UDF in VBA e VBA pasting 3 dimensional array into sheet

Naturalmente , se non lo stai restituendo da una UDF, dovrai costringerlo da solo. Un modo semplice per farlo senza scrivere codice loop è quello di fare questo:

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

Questo è solo chiamando di Excel built-in funzione di INDEX, e gli zeri significa che si desidera tornare tutti i file e tutte le colonne . Excel costringerà automaticamente la matrice 1-D di array 1-D a un array 2D. (Avvertenza: ci sono alcune limitazioni di dimensioni, ma sono molto più grandi di 10x20.)

+0

Non ho finito per usare o provare questo perché la mia soluzione è stata abbastanza buona per i miei bisogni questa volta. A prima vista, questo sembra un modo più elegante ed efficiente per fare ciò che volevo. – user392520

2

La parola "trasposizione" salta immediatamente alla mente. È sufficiente inserire i dati nell'array 2D sfogliando le colonne e le righe (ovvero la trasposizione), consentendo in pratica di rendere n (ora il numero di colonne, ma memorizzando i valori delle righe) più grande quando richiesto.

Per fare riferimento ai valori, ad esempio in un doppio ciclo, scambiare gli indici attorno.Per esempio. piuttosto vai da i = 1 a n e j = da 1 a m dove fai riferimento al valore (i, j), usa i = 1 a me j = 1 a n.

5

Se sei uno sviluppatore, qual è la differenza tra righe e colonne? Usando array (N, 2) (se si dispone di 2 colonne) è lo stesso array (2, N) - per il quale è possibile

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

E la differenza per voi (come sviluppatore) sarà quello di mettere la variabile dal ciclo al secondo posto, al posto del primo:

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

Oppure si desidera che questo:

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

Qual è la differenza?

+0

Beh, la differenza è che potrebbe essere necessario modificare ENTRAMBI le dimensioni. O non so quale dimensione sarà influenzata. – Ister

+0

@Ister, Se si utilizza la parola chiave Mantieni, è possibile ridimensionare solo l'ultima dimensione di matrice e non è possibile modificare il numero di dimensioni. https://msdn.microsoft.com/en-us/library/office/gg251578.aspx La regola è valida non solo per Office 2013 e versioni successive, come nel link sopra, ma per qualsiasi Office a partire dal 2003 (I non ho esperienza con versioni precedenti). –

+0

Sono pienamente consapevole di questa limitazione. Quello che intendevo è che non posso semplicemente trasporre la matrice ogni volta o essere felice con la possibilità di modificare solo l'ultima dimensione della dimensione. – Ister

4

Un modo in cui è possibile appenderlo è effettivamente una doppia trasposizione con una modifica del numero di colonne intermedie. Questo tuttavia funzionerà solo per gli array bidimensionali. E 'fatto come segue:

' 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) 

Naturalmente m e n si può dedurre come segue:

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

in modo da utilizzare la funzionalità di trasposizione built-in di Excel stesso. Come menzionato nei commenti al codice, questo non funzionerà con matrici di ordine superiore.

+0

Grazie. Sono stupito di quanto sia veloce. 0.08sek per 16200 righe * 14 colonne = 226000 valori (2x trasposizione, 1x redim) –

0

coercing o Slicing non sembra funzionare con Index (o Match (Index (quando voglio filtrare array (senza loop) in base a più criteri, quando la dimensione dei dati si estende su più di 2^16 righe (~ 92000 righe).

Run-Time error '13': 

Type Mismatch 

Trasposizione non funziona con grandi set di record e così anche doppia Trasposizione non funziona. non c'è comunque di filtrare una matrice e afferrare i dati senza ricorrere a cicli multipli?

sono pensando di provare il modo dizionario o ADO con Excel

0

Un array con 2 dimensioni, in cui il numero di colonne sono fissi e dinamico, può essere creato il numero di righe simili:

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 letto dalle celle A1: A10, se c'è un valore nella cella Ax, esegue il redim dell'array con +1 e aggiunge Tempvalue a array col1, aggiunge contenuti in Bx a array col2 e contenuti in Cx a array col3. Se la lunghezza del valore Ax è 0, non aggiunge nulla alla matrice.

Debug.print mostra i risultati nella "finestra immediata" nell'editor VB.

Senza le linee di prova, e l'aggiunta di un data-range dinamico il codice può essere:

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