Agire su intervalli discontinui : Funzione Min() su gruppi discontinui di valori
.
-
pagina vista: volte
Ovvero: ricercare
il valore più basso in intervalli discontinui.
Requisiti:
conoscenza dei cicli For..Next e While..Wend - utilizzo della proprietà
WorksheetFunction
Un interessante
esercizio, semplice tutto sommato, che soddisfa un'esigenza espressa da un
"pellegrino", ma che potrà interessare per le implicazioni risolvibili; si dispone
di una tabella come l'esempio sotto: in una colonna (la A) abbiamo dei
valori, nella colonna B abbiamo degli indici (ma potrebbero essere nomi,
date, orari, codici, ecc.ecc.) e comunque siano, li dobbiamo vedere come dei "criteri" di
scelta, di identificazione.
Come si nota, nella colonna B, i valori (gli "1"
o gli "0" sono i "criteri") sono raggruppati, conseguenti tra loro, ma a gruppi discontinui,
e senza un preciso ordine o sequenza tra gruppi.
|
A |
B |
C |
|
1 |
1500 |
0 |
|
|
2 |
2100 |
0 |
|
|
3 |
1785 |
1 |
954 |
|
4 |
1520 |
1 |
|
|
5 |
2200 |
1 |
|
|
6 |
954 |
1 |
|
|
7 |
1002 |
0 |
|
|
8 |
1985 |
0 |
|
|
9 |
1000 |
0 |
|
|
10 |
1420 |
1 |
850 |
|
11 |
850 |
1 |
|
|
12 |
978 |
1 |
|
|
13 |
1189 |
1 |
|
|
14 |
1000 |
1 |
|
|
15 |
2020 |
0 |
|
|
Si vuole: per ogni
gruppo di "1" nella colonna B (celle in giallo), ottenere il Minimo valore
presente nei dati correlati al gruppo di "1", presenti nella colonna A
(celle in verde). Il Minimo trovato, lo vogliamo porre nella colonna C,
nella riga iniziale del primo "1" di ogni gruppo, che evidenzio in azzurro.
Quindi nella Cella C3 ottenere il valore 954 che è il Minimo tra i valori
presenti in A3:A6, nella cella C10 ottenere il valore 850 che è il valore
Minimo tra quelli in A10:A14, e così via per quanto sarà lunga tutta la
tabella dati.
Cerchiamo, tramite
la proprietà WorksheetFunction (vedi
articolo su questo sito "Funzioni Foglio in vba") un valore minimo (funzione
MIN() ), ma potremmo cercare il massimo, oppure
ottenere il totale dei valori correlati ad un criterio, oppure la media dei
valori correlati, ecc. ecc..
L'esercizio deve
servire soprattutto a:
-
identificare ogni
intervallo contenente i "criteri" di ricerca (nell'esempio: gli
intervalli contenenti i gruppi di "1"), memorizzando gli estremi
dell'intervallo.
-
gli estremi
dell'intervallo ci serviranno per ricavare i relativi estremi di riga con i
quali comporremo gli indirizzi di un'altro intervallo contenente i valori
interessati ad una qualche azione (nell'esempio: ricavare il valore minimo
presente, ma potremmo voler copiare i valori, oppure grassettare i fonts, o
modificarne il colore, o ancora incrementarli di una certa percentuale, o
ancora....). Per identificale gli intervalli useremo la sintassi Cells(riga,colonna).
-
rendere ciclica
l'azione, quindi ripeterla per quanti saranno i "gruppi" di "criteri".
Come procediamo:
-
iniziamo un ciclo
For..Next che inizia dalla riga 1 proseguendo fino all'ultima riga occupata,
cerchi nella colonna B le celle contenenti il criterio scelto,
cioè il numero 1
-
una volta trovato
il primo 1, ne memorizziamo la riga in una variabile (Inizio), quindi iniziamo un
secondo ciclo interno che scorra le celle a seguire, colonna B, fino a che
non si trova un valore diverso (lo zero); di questo memorizziamo la riga - 1
in una altra variabile (Fine); le due variabili ci daranno quindi gli estremi di
riga dell'intervallo del "gruppo" di 1 trovati.
-
a questo punto
otteniamo un intervallo i cui estremi di riga sono formati dalle variabili
sopra ottenute, nella colonna A, e usiamo questo intervallo come argomento
nella funzione WorksheetFunction.Min(), che posizioniamo nella prima riga
dell'intervallo di 1 trovato, ma nella colonna C.
-
Ora dobbiamo
proseguire nella ricerca degli 1, proseguendo nel ciclo For...Next, ma
dobbiamo "scalare", continuando dall'ultima riga del primo gruppo di 1
(variabile "Fine"), quindi rendiamo il contatore "N" del ciclo uguale al
numero di questa ultima riga. In questo modo scorriamo tutta la tabella
"indicizzando" di volta in volta il numero di riga dal quale far continuare
lo scorrimento del ciclo in funzione della "lunghezza" del gruppo di 1
(criterio) trovato.
Vediamo la
routine: (le variabili, non dichiarate, sono tutte di tipo Variant)
Sub MinimoScalato()
Uro = [B65536].End(xlUp).Row 'prendiamo
il numero dell'ultima riga occupata colonna B (limite sup. del
ciclo)
Fine = 0 'impostiamo a zero
la variabile Fine che indicherà l'ultima riga occupata da un gruppo
di 1 letti
Cont = 0 'impostiamo a zero
la variabile Cont che usiamo per incrementare il n. riga nel ciclo
interno While..Wend
Inizio = "" 'impostiamo a "vuoto"
la variabile Inizio che indicherà la riga di ogni primo 1 trovato in
B
For N = 1 To Uro 'iniziamo il
ciclo esterno che scorrerà le celle, e "N" sarà il numero riga della
cella scorsa
If Cells(N, 2) = 1 Then
'controlliamo se il valore della cella ora letta è uguale al
"criterio", cioè 1; se uguale allora
Inizio = Cells(N, 2).Row 'con
"Inizio" memorizziamo il numero di riga
Cont = Inizio 'e
rendiamo la variabile Cont, che era a zero, al valore di riga letto
con Inizio
While Cells(Cont, 2) <> 0
'quindi iniziamo il ciclo While che scorrerà le celle fino a che
sono diverse da zero
Cont = Cont + 1
'incrementiamo il numero di riga Cont di
una unità fino a che...
Wend '...raggiunta la
condizione che il valore letto non è diverso da zero, quindi quando
sono finiti gli 1, termina il 'ciclo While
Fine = Cont - 1 ' assegniamo a
"Fine" il numero di riga ora trovato, meno 1 (quindi l'ultimo 1 del
gruppo)
Myrange = Range(Cells(Inizio, 1), Cells(Fine, 1))
'e impostiamo la variabile Myrange come
intervallo su cui valutare la 'funzione Min()
Cells(Inizio, 3) = Application.WorksheetFunction.Min(Myrange)
'quindi si assegna il risultato alla
cella(riga "Inizio", 'colonna 3, la C)
N = Fine 'e ora si rivaluta il
contatore N, impostandolo al valore di Fine
End If
Cont = 0 'quindi si riazzera la
variabile Cont
Next
End Sub |
Ricapitolando, la cosa forse più interessante, è l'aver reso modificabile l'intervallo su cui agisce la variabile Myrange, grazie alla sintassi Cells e agli indici "Inizio" e "Fine", dove gli indici di riga si adattano all'intervallo corrispondente ai criteri trovati nel ciclo While..Wend.
Giusto per i meno esperti in tema di cicli, chiarisco che in un ciclo For..Next,
le istruzioni si ripetono tante volte quanto indicato dai due limiti del
ciclo, e per quello usato sopra: da 1 fino alla fine dell'elenco; la
variabile che identifica il contatore del ciclo, nell'esempio la variabile "N", assume quindi all'inizio il valore minimo indicato, cioè 1, e si
incrementa naturalmente di 1 ad ogni Next, (che vorrebbe dire: passa al
secondo valore tra quelli previsti dai limiti) . Se usiamo quindi la
variabile "N" come riferimento alla riga, nella sintassi Cells(N, colonna),
ci spieghiamo come si possano scorrere tutte le celle di un'intervallo,
infatti N sarà all'inizio 1, poi ad ogni Next 2, poi 3, ecc. ecc.. Se però
dobbiamo intervenire per "modificare" l'indice di riga perchè dobbiamo
passare ad una determinata riga, possiamo reimpostare il valore del
contatore come nell'esempio sopra dove N, che al momento in cui si trova il
primo criterio 1, equivaleva a 3 (il primo 1 è nella terza riga), avevamo
bisogno di passare col successivo Next, non già alla riga 4, ma alla fine
del gruppo degli 1 iniziato dalla riga 3, cioè alla riga rappresentata da
"Fine", il cui valore l'abbiamo reperito con in ciclo interno While...Wend.
Buon lavoro.
prelevato sul sito
www.ennius.altervista.org |