Lavorare con le matrici dinamiche

Ultima modifica: 29-05-2016

 

Il lavoro presentato negli ultimi due articoli, visto nel suo insieme, forse è stato un po' dispersivo. Nasce l'esigenza di scrivere due righe come riflessione su quello che abbiamo fatto.

Indicazioni generali

Oramai penso sia consolidato il concetto della utilità, almeno in certi lavori, di lavorare con le matrici che altro non sono che delle variabili aggregate tra loro e a cui ci si può riferire facendo uso degli indici.

Anche se solo idealmente, è bene abituarci all'idea di considerare le variabili e le matrici allo stesso modo delle celle di un foglio di lavoro di Excel.

 

In un foglio di lavoro di Excel possiamo leggere i dati singolarmente servendoci degli appropriati indici di riga e colonna

MioDato = Cells(1, 2)

oppure in forma tabellare dando il compito di generare gli indici di riga e colonna a cicli opportunamente istruiti

For R = PrimaRiga To UltimaRiga
For C = PrimaColonna To UltimaColonna
MioDato = Cells(R, C)
Next
Next

Alla stessa maniera possiamo comportarci con le matrici.

Leggendo le istruzioni sopra riportate è lecito immaginare (dico solo immaginare) di leggere invece di Cells il nome di una matrice e che gli indirizzi di riga e colonna usati per riferirsi a specifiche locazioni del foglio siano gli indici delle due dimensioni di una matrice a cui ci riferiamo per determinare le locazioni della matrice.

Progettazione di una matrice

Come in un foglio di Excel possiamo impostare una tabella basandoci sulle righe o sulle colonne e scegliendo la disposizione dei dati in esse, altrettanto possiamo fare con le matrici.

Ma attenzione: mentre su un foglio di Excel la scelta può essere arbitraria (disposizione di una tabella con i record lungo le righe ed i campi disposti nelle colonne o viceversa), alcune volte non può essere altrettanto arbitraria la scelta della disposizione di dati tabellari in una matrice.

Questo è il caso di una matrice dinamica a due dimensioni.

Normalmente siamo abituati a vedere un database come una serie di righe che ospitano i vari record e di colonne che accolgono i campi. Si potrebbe immaginare così una tabella in un foglio di Excel e così possiamo immaginare una tabella memorizzata in una matrice.

  Campo 1 Campo 2 Campo 3
Record 1      
Record 2      
Record 3      
Record 4      
Record 5      
Record 6      
Record 7      

Questa disposizione può andare bene per una matrice, statica o dinamica che sia, che tuttavia, una volta creata, non deve essere più modificata.

Ma, se durante l'attività è necessario aggiungere o rimuovere record, questa disposizione non va più bene perchè in questi casi per ridimensionare dinamicamente la matrice senza perdita di dati dobbiamo usare la sintassi: ReDim Preserve Matrice (Indici) e l'unica dimensione che si può ridimensionare deve essere l'ultima.

L'uso della parola chiave Preserve, infatti, permette di estendere o ridurre solo l'ultima dimensione. Nell'esempio di una matrice progettata come quella rappresentata nella tabella sopra la giusta sintassi deve essere:

ReDim Preserve Matrice (1 To 7, 1 To 3 + 1)

E' ovvio che, in una matrice costruita come quella rappresentata sopra, questa istruzione effettua il ridimensionamento nella direzione dei campi, per noi inutile, anzi dannosa. Nella gestione di un database, abbiamo la necessità di ridimensionare la matrice nella direzione dei record.

Perciò è necessario progettare la matrice come mostrato nella seguente tabella:

  Record 1 Record 2 Record 3 Record 4 Record 5 Record 6 Record 7
Campo 1              
Campo 2              
Campo 3              

In questo modo possiamo ridimensionare dinamicamente la matrice nella direzione dei record con ReDim Preserve:

ReDim Preserve Matrice (1 To 3, 1 To 7 + 1)

Dimensionare, popolare e leggere una matrice dinamica

Normalmente per popolare o leggere una matrice a due dimensioni usiamo servirci di due cicli nidificati:

  1. il primo per puntare ai suoi record (righe),
  2. il secondo per leggere, nella riga puntata dal primo ciclo, i vari campi (disposti nelle varie colonne).

Un banale esempio di memorizzazione di dati in una matrice e relativa visualizzazione del suo contenuto potrebbe essere il seguente:

Sub RiempiMatrice1()
Dim Matrice(), NumCampi, NumRec, Record, Campi
Dim Intervallo As Range
With Range("a1").CurrentRegion
NumRec = .Rows.Count - 1
NumCampi = .Columns.Count
Set Intervallo = .Offset(1, 0).Resize(NumRec, NumCampi)
End With
'--------------------------------------------------------------
' dimensioniamo opportunamente la matrice

ReDim Matrice(1 To NumRec, 1 To NumCampi)
' quindi la riempiamo
For Record = 1 To NumRec
For Campi = 1 To NumCampi
Matrice(Record, Campi) = Intervallo(Record, Campi)
Next
Next
' poi ne leggiamo il contenuto: in questo esempio trascriviamo i dati memorizzati
' nella matrice 10 colonne più in la da cui sono stati prelevati

For Record = 1 To NumRec
For Campi = 1 To NumCampi
Intervallo(Record, Campi + 10) = Matrice(Record, Campi)
Next
Next
End Sub

Nell'esempio qui sopra, prima di utilizzare la matrice determiniamo le sue dimensioni leggendo le dimensioni dell'intervallo da cui dover prelevare i dati.

 

Dimensionare, popolare e leggere una matrice dinamica con Preserve

Purtroppo non è sempre possibile determinare a priori la quantità di dati da dover memorizzare nella matrice.

In questi casi abbiamo la possibilità di ridimensionare dinamicamente la matrice man mano che leggiamo i dati da memorizzare.

Per questo lavoro ci viene in aiuto la parola chiave Preserve

Purtroppo c'è un limite: usando ReDim Preserve possiamo ridimensionare solo l'ultima dimensione della matrice.

Con la routine vista qui sopra abbiamo creato una matrice a due dimensioni dimensionandola in questo modo:

ReDim Matrice(1 To NumRec, 1 To NumCampi)

ossia, nella prima dimensione abbiamo i record e nella seconda i campi:

  campo 1 Campo2
Record 1 Giuliana P.za della Pace, 40
Record 2 Michele Via Tulipani, 24
Record 3 Gabriele viale dello Splendore
Record 4 Gabriele Lungomare Rodi
Record 5 Stefano via G. Leopardi
Record 6 Marcello via Milano 13
Record 7 Giuliana via Cona

Per poter lavorare una matrice con la parola chiave Preserve occorre che la matrice sia creata in altra maniera, e cioè come mostrato in questa tabella:

  Record 1 Record 2 Record 3 Record 4 Record 5 ecc
campo 1 Giuliana Michele Gabriele Gabriele Stefano ecc
campo 2 P.za della Pace, 40 Via Tulipani, 24 viale dello Splendore Lungomare Rodi via G. Leopardi ecc

 

Questo che segue è un esempio di come memorizzare i dati in una matrice che usa la parola chiave Preserve:

Sub RiempiMatrice2()
Dim Matrice(), NumCampi, NumRec, Record, Campi
Dim Intervallo As Range
With Range("A1").CurrentRegion
NumRec = .Rows.Count - 1
NumCampi = .Columns.Count
Set Intervallo = .Offset(1, 0).Resize(NumRec, NumCampi)
End With
'-------------------------------------------------------
' ora riempiamo la matrice usando la parola chiave Preserve
For Record = 1 To NumRec
ReDim Preserve Matrice(1 To NumCampi, 1 To Record)
For Campi = 1 To NumCampi
Matrice(Campi, Record) = Intervallo(Record, Campi)
Next
Next
' poi ne leggiamo il contenuto: in questo esempio trascriviamo i dati memorizzati
' nella matrice 10 colonne più in la da cui sono stati prelevati

For Record = 1 To NumRec
For Campi = 1 To NumCampi
Intervallo(Record, Campi + 10) = Matrice(Campi, Record)
Next
Next
End Sub

Come vedete i due cicli (quello esterno per i record e quello intervo per i campi) sono posti proprio come nella prima routine.

Questi sono i passaggi principali da compiere:

Ad ogni avanzamento del ciclo esterno (quello dei record) viene incrementata la seconda dimensione della matrice:

ReDim Preserve Matrice(1 To NumCampi, 1 To Record)

All'interno del secondo ciclo, quel che viene incontrato nel normale flusso dei dati di origine, viene memorizzato nella matrice in maniera inversa:

Matrice(Campi, Record) = Intervallo(Record, Campi)

Il dato trovato nella posizione RC del foglio viene memorizzato nella posizione CR della matrice:

R C   R C  
1 1 viene memorizzato in 1 1 (ovvio all'inizio)
1 2 viene memorizzato in 2 1  
2 1 viene memorizzato in 1 2 incr di riga
2 2 viene memorizzato in 2 2  
3 1 viene memorizzato in 1 3 incr di riga
3 2 viene memorizzato in 2 3  
           

Lo stesso lavoro viene eseguito per visualizzare i dati memorizzati nella matrice.

Vengono istruiti i due cicli nidificati nell'ordine Riga Colonna e poi si esegue:

Intervallo(Record, Campi + 10) = Matrice(Campi, Record)

usando lo stesso schema di prima.

 

Alcuni modi di trattare una matrice dinamica

Inserire in modo dinamico un record in mezzo alla matrice

E' possibile inserire un nuovo record in mezzo alla matrice.

Abbiamo una matrice di questo tipo:

  Record 1 Record 2 Record 3 Record 4 Record 5 Record 6 Record 7 Record 8
Campo 1                
Campo 2                
Campo 3                

Vogliamo inserire un nuovo record nella quarta posizione:

Per consentire alla matrice di ospitarfe un nuovo record incrementiamo la sua seconda dimensione con un:

NumRec = UBound(Matrice, 2) + 1
ReDim Preserve Matrice(1 To NumCampi, 1 To NumRec)

  Record 1 Record 2 Record 3 Record 4 Record 5 Record 6 Record 7 Record 8 Nuovo spazio
Campo 1                  
Campo 2                  
Campo 3                  

Spostiamo verso il basso tutti i record a cominciare dal n° 8 fino ad arrivare al n° 4. In questo caso è necessario procedere dal basso iniziando dal penultimo record perchè altrimenti l'operazione sarebbe molto più complessa, se non impossibile.

For Record = NumRec - 1 To 4 Step -1
For Campi = 1 To NumCampi
Matrice(Campi, Record + 1) = Matrice(Campi, Record)
Next
Next

Fatto questo ci ritroviamo in questa situazione con il record n° 4 duplicato:

  Record 1 Record 2 Record 3 Record 4 Record 4 Record 5 Record 6 Record 7 Record 8
Campo 1                  
Campo 2                  
Campo 3                  

Fatto questo vengono sostituiti i contenuti del record n° 4 aggiungendo quelli nuovi

For Campi = 1 To NumCampi
Matrice(Campi, 4) = "quel che vuoi"
Next

ottenendo finalmente la matrice opportunamente ridimensionata e coi nuovi dati inseriti:

  Record 1 Record 2 Record 3 Record 4 Record 5 Record 6 Record 7 Record 8 Record 9
Campo 1                  
Campo 2                  
Campo 3                  

 

Modificare un record

Questa è senz'altro la più semplice tra le operazioni da compiere su una matrice.

Stabilito quale è il record da modificare lo si sovrascrive semplicemente:

RecordDaModificare = 7
For Campi = 1 To NumCampi
Matrice(Campi, RecordDaModificare) = "quel che vuoi"
Next

 

Eliminare un record

La procedura è simile a quella già vista per l'inserimento di un record in mezzo alla matrice, solo che ora, invece di aggiungere un record, lo dobbiamo eliminare.

Si procede in questo modo:

Si individua il record da eliminare:

RecordDaEliminare = 2

Quindi, partendo da questo record fino al penultimo, si spostano in sù tutti i record sottostanti:

For Record = RecordDaEliminare To NumRec - 1
For Campi = 1 To NumCampi
Matrice(Campi, Record) = Matrice(Campi, Record + 1)
Next
Next

Si toglie una unità al numero totale dei record:

NumRec = UBound(Matrice, 2) - 1

e si ridimensiona la matrice. Questa operazione fa perdere l'ultimo record che risulta il doppione del penultimo grazie agli spostamenti effettuati.