Confrontare elenchi (colonne) di dati con aree (intervalli) contenenti dati. - pagina vista: volte

Requisiti: conoscenza dei cicli For..Next

In genere, quando si ricercano e confrontano dati tra due tabelle, si utilizzano cicli For...Next oppure For Each...Next che, scorrendo riga per riga (o colonna per colonna) di una tabella, controlli se il dato in quel momento letto sia presente nelle righe (o colonne) dell'altra tabella, magari utilizzando un secondo ciclo per scorrere ogni riga (o colonna) di questa seconda tabella. Questo modo di agire (con doppi cicli) su intervalli molto ampi possono richiedere molto tempo, specie se il computer è un pò "vecchiotto" o poco potente. Vediamo un possibile esempio in questa immagine: nella colonna E (verde) abbiamo dei nomi; nell 'intervallo A1:D4 un elenco ancora con nomi; in giallo ho evidenziato i nomi simili a quelli presenti nella colonna E.

Ora, il modo di impostare istruzioni dipende ovviamente da ciò che vogliamo ottenere da un confronto, ma supponendo, giusto per restare nel tema di un doppio ciclo, di voler sapere in quali celle del Range A1:D4 è presente ogni nome letto nella colonna E, potremmo impostare un'istruzione come questa sotto, dove per ogni nome letto in E dal ciclo esterno (For RigaE = 1 To 4), si effettua un secondo ciclo interno che scorre tutte le celle dell'intervallo A1:D4 (For Each CL ecc.ecc); se il nome letto viene trovato, se ne prende l'indirizzo cella (Address), e lo si concatena alla variabile "Indirizzo" impostata inizialmente a vuota; alla fine di ogni ciclo interno, restituiamo un messaggio (in totale 4 messaggi), tipo quello che vediamo sotto a destra:

Sub DimmiIndirizzi()
Dim RigaE
Indirizzo = ""
For RigaE = 1 To 4
 'primo ciclo esterno
For Each CL In Range("A1:D4")
 'secondo ciclo interno
If Cells(RigaE, 5) = CL Then
indirizzo = Indirizzo & CL.Address & " "
End If
Next 
MsgBox Cells(RigaE, 5) & " è presente in " & Indirizzo
Indirizzo = ""  
'si vuota la variabile Indirizzo
Next
End Sub

Se invece dell'indirizzo cella del valore uguale trovato in A1:D4 si volesse contare quante volte è presente ogni valore letto nell'intervallo colonna E, anzichè usare un doppio ciclo, potremo eseguire un ciclo semplice, che scorra la colonna E e confronti il dato letto con tutto l'intervallo A1:D4 (senza doverlo scorrere cella dopo cella) prendendolo come "insieme" (intervallo) di celle (Range) al quale applicare, in questo caso, la funzione CountIf (Conta.Se), come nella seguente macro dove facciamo restituire per ogni dato letto dal ciclo nella colonna E, il valore del dato letto in E (il nome) concatenato al valore numerico di quante volte è presente:

Sub Presenza()
Dim R
Dim Presenze
For R = 1 To 4
Presenze = Application.CountIf(Range("A1:D4"), Cells(R, 5))
MsgBox Cells(R, 5) & " è presente " & Presenze & " volte"
Next
End Sub

Come si nota, nella macro Sub Presenza(), non si scorrono tutte le celle dell'intervallo A1:D4 con un ciclo, ma ci si riferisce all'intero intervallo, confrontandolo in un colpo solo con ciò che viene letto nella Cella(R, 5) ottenendo una risposta più rapida e comunque completa.

E' questo modo di riferirci ad un intero intervallo che si vuole mettere in evidenza in questo articolo, intervallo che rappresenta un argomento (dove cercare) di una Funzione che useremo (nell'esempio la Funzione CountIf(intervallo dove cercare, cosa cercare)).

Quando sia possibile, è dunque preferibile utilizzare un riferimento ad un intero intervallo piuttosto che usare un ciclo che scorra le tutte le celle del medesimo intervallo. Un altra considerazione che dobbiamo fare, è che possiamo usare due sintassi diverse per identificare un'intervallo:

  • usando il riferimento in stile A1 Range("A1:D4")

  • usando la sintassi Cells  :   Range(Cells(1, 1), Cells(4, 4))

Infatti se modifichiamo la riga sopra in  Presenze = Application.CountIf(Range(Cells(1, 1), Cells(4, 4)), Cells(R, 5)) il risultato non cambia.

Entrambi identificano lo stesso intervallo, ma l'utilizzo di Cells (che richiede di indicare il numero di riga e il numero di colonna di una specifica cella), risulterà vantaggioso se avremo bisogno di scorrere le celle dell'intervallo o meglio, di modificare gli indici di riga o di colonna.

Per evitare contraddizioni, (Ciclo contro Riferimento a intervallo) è necessario precisare che in alcuni casi è necessario utilizzare la sintassi Cells per riferirsi ad un intervallo, quando l'intero intervallo deve variare. Una richiesta di un "pellegrino" ci fornisce il motivo di chiarire.

La domanda: "...Ho scritto questa macro che confronta il contenuto delle celle E1->E4 con il range A1->D4 e se trova stringhe uguali le cancella.
La macro lavora su quattro righe, ed io avrei bisogno di inserirla in un ciclo for next in modo da farla lavorare sulle quattro righe successive ad es e5->e8 con il range a5->d8 e così via per 31 volte senza dovere riscrivere la stessa istruzione per 31 volte consecutive...."

e questa la sua macro:

  • Sub Confronta()
    Row = 1
    For E = Row To 4
    Range("A1:D4").Replace What:=Cells(E, 5), Replacement:="", LookAt:=xlPart, _
            SearchOrder:=xlByRows, MatchCase:=False
            Next
    End Sub

Come si nota, le istruzioni usano il metodo Replace per rimpiazzare col vuoto ("") le celle in A1:D4 che saranno uguali al valore letto nel ciclo nell'intervallo E1:E4. La sua istruzione funziona egregiamente, ma vista la richiesta, è necessario usare la sintassi Cells per identificare l'intervallo in cui cercare, visto che dobbiamo incrementare di 4 in 4 gli indici di riga; come pure dovremo incrementare di 4 in 4 gli indici di riga dell'intervallo delle celle da cercare (colonna E).

Creiamo quindi un doppio ciclo: il ciclo esterno (primo ciclo) che farà scorrere gli indici di riga dell'intervallo dove cercare, con passo 4 (Step 4) e all'interno del ciclo esterno un secondo ciclo che scorrerà le celle della colonna E, confrontandole con l'intervallo in quel momento individuato; questa l'istruzione impostata per 40 righe:

Sub Confronta()
Dim R
Ro = 1
 'impostiamo a 1 la variabile Ro usata come limite inferiore del ciclo interno che scorre la colonna E
Ru = 4  '
impostiamo a 4 la variabile Ru usata come limite superiore del ciclo interno che scorre la colonna E
For R = 1 To 40 Step 4 
'si inizia il ciclo che indicizza le righe dell'intervallo dove cercare, incrementando di 4 in 4
For E = Ro To Ru   
'si inizia il ciclo interno che scorre le celle nella colonna E
'
sotto: si usa la sintassi Cells per identificare l'intervallo dove cercare, e si applica il metodo Replace
Range(Cells(R, 1), Cells(R + 3, 4)).Replace What:=Cells(E, 5), Replacement:="", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False
Next
Ro = Ro + 4  
'ad ogni termine di ciclo interno, si incrementano di 4 gli indici riga (estremi del ciclo) per la colonna E
Ru = Ru + 4
Next
End Sub


 

le prime 20 righe in origine

dopo aver lanciato la macro

Il bello dell'azione è che l'eliminazione dei dati uguali, nell'intervallo dove cercare (con celle gialle) avviene in contemporanea su tutti i dati simili (la parola "pluto" era presente 3 volte e su righe e colonne diverse, e viene eliminata in contemporanea da tutte le celle dell'intervallo).

A questo punto l'esercizio si presta anche ad altri tipi di interventi, oltre a quello di eliminare dati uguali e quelli letti, sarà possibile per esempio, mettere tutto in maiuscolo i dati trovati uguali nell'intervallo, come in questa routine impostata su un Range definito:

  • Sub Maiuscole()
    Row = 1
    For E = Row To 4
    Testo = UCase(Cells(E, 5))  
    'impostiamo maiuscolo il dato letto in colonna E, e rimpiazziamo nell'intervallo A1:D4 il dato uguale, sostituendolo con il contenuto 'della variabile "Testo"
    Range("A1:D4").Replace What:=Cells(E, 5), Replacement:=Testo, LookAt:=xlPart, _
    SearchOrder:=xlByRows, MatchCase:=False
    Next
    End Sub

o ancora eseguire modifiche a valori numerici, di valori uguali a quelli letti, moltiplicandoli (o altra operazione aritmetica) per un moltiplicatore, come questa sotto, dove moltiplichiamo il valore trovato uguale, per 5:

  • Sub Moltiplica()
    Row = 1
    For E = Row To 4
    Valore = Cells(E, 5).Value
    Range("A1:D4").Replace What:=Cells(E, 5), Replacement:=Valore * 5, LookAt:=xlPart, _
    SearchOrder:=xlByRows, MatchCase:=False
    Next
    End Sub


Per il metodo Replace applicato all'oggetto "Range", leggete eventualmente l'articolo su questo sito : "Il Metodo Replace".

 

Buon lavoro.

prelevato sul sito www.ennius.altervista.org