Le Matrici e la Funzione Filter

 

Una premessa doverosa

Più volte in queste pagine abbiamo scritto degli appunti sulle matrici. Ma ogni volta, come è logico, vengono presentate da un diverso punto di vista o viene presentato un modo diverso di lavorare con queste nostre grandi amiche.

Il motivo per cui io personalmente insisto sull'argomento è perchè lavorare con le matrici, anziché lavorare direttamente sui dati già memorizzati nelle celle di un foglio di Excel, specialmente se i dati sono numerosi, è da ricercare nel fatto che questa metodologia ci fa risparmiare tempo. Sembra strano in quanto, per lavorare con le matrici, dobbiamo compiere più azioni che lavorare semplicemente sui dati già residenti in celle:

  1. raccogliere e memorizzare i dati nella matrice

  2. elaborare i dati

  3. riscrivere i dati sul foglio

ma è così e ve lo dimostro:

per eseguire l'ordinamento di un elenco di 500 voci direttamente nelle celle, col mio computer impiego circa 19''
per memorizzare 2000 dati in una matrice, eseguirne l'ordinamento, riscrivere i dati ordinati nelle celle, ne impiego solo 2''

Per la questione sull'ordinamento vi rimando all'articolo "Ordinare i dati". Spero che abbiate un elenco o una tabella che abbia un numero considerevole di dati con cui testare la mia convinzione.

 

La funzione Filter

Questa volta vogliamo esaminare una funzione che, se applicata ad una matrice ad una dimensione, ci restituisce un'altra matrice, a base 0 (zero), che contiene gli elementi che contengono una sottostringa usata come stringa di ricerca.

Si tratta della funzione Filter e la sintassi è:

NuovaMatrice = Filter(sourcearray, match[, include[, compare]]).

La funzione cerca tutte le stringhe memorizzate  in sourcearray e che contengono o non contengono (dipende dall'argomento include) un insieme di caratteri memorizzati nell'argomento match e le restituisce in un'altra matrice a base 0 (zero). Se in sourcearray non viene trovata alcuna corrispondenza di match, la funzione Filter restituisce una matrice vuota.

In pratica da una matrice che contiene una lista di nomi è possibile estrarre o escludere tutti i nomi che iniziano, contengono o finiscono con una determinata sequenza di caratteri.

 

Ma prima di andare avanti debbo esporre alcuni limiti che ho riscontrato:

  1. le due matrici, sourcearray e NuovaMatrice, non possono rimanere di tipo Variant ma debbono essere dichiarate come String altrimenti provocano un errore "Tipo non corrispondente":
            Dim sourcearray() As String, NuovaMatrice() As String

  2. la matrice sourcearray deve essere ad una sola dimensione perchè nella funzione non viene supportata una matrice a più dimensioni

  3. la matrice restituita dalla funzione è a base 0 (zero) vale a dire che i dati memorizzati  iniziano dall'indice 0 (zero) della matrice anche se la abbiamo dichiarata con un indice inferiore diverso da 0

  4. match non accetta caratteri jolly (* ? e quant'altro) perchè inutile

Con match = "ri":

se include è impostato a True verranno richiamati: Gabriele, Maria, Mario, Rino, Vittorio, Marina, Gori, Riccardo, ecc

se include è impostato a False i nomi appena visti verranno esclusi ma verranno richiamati tutti gli altri nomi che non includono la stringa "ri": Marcello, Giuliana, Alessandra, Stefano, Michele, ecc

Omettendo l'ultimo argomento (compare) si possono ricercare o escludere parole in base ad una corrispondenza esatta (tenendo conto anche delle maiuscole e minuscole) tra le due parti: sourcearray e match.

Soluzione

Vi mostro ora un esempio su come filtrare una matrice usando due metodi diversi:

Ho suddiviso le due routines in tre blocchi:

 

Il primo metodo: basato sulla funzione Instr.

Tralasciamo la spiegazione del primo e del terzo blocco di istruzioni per soffermarci ad osservare quel che succede nel secondo blocco che è quello principale e dove viene usata la funzione Instr per cercare le corrispondenze tra le due parti in gioco.

La funzione Instr ha la seguente sintassi:

InStr([inizio, ]stringa1, stringa2[, confronto])

inizio determina la posizione di partenza per la ricerca

dove stringa1 è la stringa che è l'oggetto della ricerca

stringa2 è la stringa da cercare in stringa1

confronto determina se la ricerca deve essere eseguita

La funzione restituisce la posizione della prima occorrenza di stringa2 in stringa1. Se stringa2 non viene rilevata in stringa1 restituisce 0 (zero)

  1. La variabile B ci servirà come contatore ed indice della matrice che dovrà accogliere i dati filtrati (MatriceFiltrata).

  2. Iniziamo il solito ciclo per passare in rassegna tutti i dati contenuti nella matrice Nome

  3. Se la stringa memorizzata nella variabile Criterio è compresa nel dato che si sta esaminando:

    1. viene incrementato il contatore B di una unità

    2. viene incrementata la dimensione della matrice MatriceFiltrata usando la parola chiave Preserve per non perdere i dati già memorizzati in questa matrice (la prima volta la matrice non contiene nulla e nemmeno è indicizzata: MatriceFiltrata() )

    3. infine il dato viene memorizzato nella nuova locazione della matrice

 

Sub Funz_Filter_Tradizionale()
Dim Nome() As String, MatriceFiltrata() As String
Dim A, B, UltimaRiga, Colonna, Criterio
UltimaRiga = Range("A1").End(xlDown).Row
ReDim Nome(1 To UltimaRiga - 1)
For A = 2 To UltimaRiga
        Nome(A - 1) = Cells(A, 1).Value
Next
Criterio = InputBox("Scrivi il criterio per il filtro da applicare alla matrice" _
& vbCr & "Es: ""giu"" ""ga"" ""fa""", "Attenzione")
If Criterio = "" Then Exit Sub

B = 0
For A = LBound(Nome) To UBound(Nome)
        'Così per richiamare tutti i nomi che includono la stringa contenuta in Criterio
        If InStr(1, Nome(A), Criterio, vbTextCompare) Then
        'Così per richiamare tutti i nomi che non includono la stringa contenuta in Criterio
        'If InStr(1, Nome(A), Criterio, vbTextCompare) = 0 Then
        'Così per ottenere i confronti casesensitive
        'If InStr(1, Nome(A), Criterio) Then
                B = B + 1
                ReDim Preserve MatriceFiltrata(B)
                MatriceFiltrata(B) = Nome(A)
        End If
Next

Colonna = 5
For A = LBound(MatriceFiltrata) To UBound(MatriceFiltrata)
        Cells(A + 1, Colonna + 1) = MatriceFiltrata(A)
Next
End Sub

 

Vediamo ora come ottenere lo stesso risultato usando la funzione Filter. Noterete che, in questo caso, il secondo blocco è costituito da un'unica riga ed è questa che assolve il compito. La riga non ha bisogno di commenti tranne quello di notarne la sintassi.

Con include (terzo argomento) impostato a True vogliamo che vengano accettati tutti i dati che comprendono la tringa rappresentata in Criterio

 

 

Sub Funz_Filter()
Dim Nome() As String, MatriceFiltrata() As String
Dim A, B, UltimaRiga, Colonna, Criterio
UltimaRiga = Range("A1").End(xlDown).Row
ReDim Nome(1 To UltimaRiga - 1)
For A = 2 To UltimaRiga
        Nome(A - 1) = Cells(A, 1).Value
Next
Criterio = InputBox("Scrivi il criterio per il filtro da applicare alla matrice" _
& vbCr & "Es: ""giu"" ""ga"" ""fa""", "Attenzione")
If Criterio = "" Then Exit Sub

'Così per richiamare tutti i nomi che includono la stringa contenuta in Criterio
MatriceFiltrata = Filter(Nome(), Criterio, True, vbTextCompare)
'Così per richiamare tutti i nomi che non includono la stringa contenuta in Criterio
'MatriceFiltrata = Filter(Nome(), Criterio, False, vbTextCompare)
'Così per ottenere i confronti casesensitive
'MatriceFiltrata = Filter(Nome(), Criterio, True)

Colonna = 5
For A = LBound(MatriceFiltrata) To UBound(MatriceFiltrata)
        Cells(A + 2, Colonna + 1) = MatriceFiltrata(A)
Next
End Sub
 

 

 

Conclusioni

Buon Lavoro

 

prelevato sul sito www.ennius.altervista.org