[vba] Creare una funzione che accetti un numero indefinito di parametri

Per questo esercizio vediamo come creare una funzione personalizzata simile (perché non andremo ad implementare tutte le alternative) al SOMMA.PIÙ.SE in VBA per Excel.

Anzitutto individuiamo le criticità:

  1. Avremo bisogno di accettare un range per la somma ed n coppie di parametri come intervalli e criteri, la nostra funzione avrà quindi la forma di FUNZIONE(somma, [intervallo 1], [criterio 1], …)
  2. I criteri possono essere numerici oppure stringhe di confronto, come per esempio “>2”

Anzitutto vediamo come creare una funzione che accetti n parametri come abbiamo definito di sopra. Per cui digitiamo in VBA:

In questo caso abbiamo inserito il minimo indispensabile, definendo il risultato come un Double, dichiarando internamente una variabile totale ed impostandola su 0. Questa variabile la utilizzeremo in seguito. L’argomento somma è di tipo Range (qui occhio agli indici, come vedremo tra poco!) e poi segue un vettore di parametri args definiti come Variant.

Per scorrere i vari parametri possiamo usare l’istruzione:

A questo punto vogliamo costruire un vettore di criteri lungo quanto l’intervallo della somma e che per ogni chiave contenga un valore equivalente a 0 oppure ad 1, in funzione che si debba o meno sommare il valore nell’intervallo somma.

Per fare questo definiamo:

Con il metodo somma.count misuriamo la dimensione del Range somma, attenzione perché il range si muove da 1 fino a somma.count, mentre il nostro vettore criteri andrà da 0 fino a somma.count – 1

Infine impostiamo tutto il vettore criteri() su 1, questo ci servirà tra poco per le operazioni logiche.

A questo punto non ci rimane che valutare tutti i criteri, tenendo presente che vanno a coppie di intervallo criteri e criterio. Perciò utilizzeremo l’istruzione:

Gli intervalli dei criteri si trovano in posizioni pari, mentre il criterio in posizione dispari.

Infine utilizzeremo il metodo Evaluate sfruttando la funzione SE di Excel per valutare ogni criterio, nel modo seguente:

Faccio notare che in Evaluate va scritta la funzione col nome in inglese. Dal momento che i criteri possono essere numerici oppure stringhe, come abbiamo detto all’inizio, dobbiamo distinguere tra le due cose e quindi scriveremo:

Infine ricordiamoci che tutti i criteri devono essere validi, quindi per modificare ogni posizione del vettore criteri() useremo il prodotto. In questo modo se sono tutti veri risulterà 1, un prodotto di tutti 1 insomma, mentre se anche un solo criterio sarà falso allora il prodotto risulterà 0, dal momento che basta uno 0 nel prodotto per azzerare tutto.

Detto tutto ciò la nostra funzione finale sarà simile a questa:

Inutile dire che la funzione potrebbe essere ulteriormente affinata per coprire tutti i possibili casi in cui può essere utilizzata SOMMA.PIÙ.SE 😉

Vedi articolo

[VBA] Aggiungere nuovo foglio con data corrente in Excel

Immaginiamo di voler aggiungere, in modo programmatico, un nuovo foglio con un nome e la data corrente, per esempio IMPORTAZIONE 10 09 2018.

Quello che vedremmo come risultato nella cartella di lavoro in Excel sarebbe qualcosa di analogo:

Per farlo costruiamo una funzione in VBA nel modo seguente:

Testiamo la funzione mettendola dentro una sub:

Come è possibile notare la funzione restituisce il riferimento al nuovo foglio creato, che possiamo utilizzare nella sub di esempio per scrivere, per esempio, dentro ad una cella.

Se volessimo aggiungere un controllo, nella solita funzione CreaNuovoFoglio, perché non ci siano fogli duplicati, sarebbe sufficiente modificare la funzione nella maniera seguente:

In questo modo al nome del foglio viene posposto un termine n-simo, in questo caso (1), (2) ecc. Se lo si desidera chiaramente si può aggiungere qualunque termine oppure decidere di interrompere l’operazione.

Vedi articolo

[vba] Classe per interrogare in SQL un foglio excel

Obiettivo: creare una classe per poter interrogare tramite query SQL un foglio excel, leggendo e scrivendo i dati come se lavorassimo con tabelle SQL

Questo esercizio riprende il punto direttamente da questo precedente: Interrogare foglio di Excel con SQL in VBA

Dopo aver visto come poter interrogare con query SQL un foglio excel, vediamo come creare una classe ad hoc che ci permetta di leggere e scrivere i dati mediante query SQL. Per farlo anzitutto aggiungiamo una nuova classe:

Premendo ALT+F11 apriamo l’editor Visual Basic, ci posizioniamo su un qualunque punto del progetto a sinistra e clicchiamo col destro (per esempio su Form), poi scegliamo Inserisci ⇒ Modulo classe. A questo punto verrà creata una nuova classe. Per rinominarla è sufficiente selezionarla e poi spostarsi nel riquadro delle proprietà.

A questo punto incolliamo dentro la classe appena creata (tenendo presente poi il nome scelto) il seguente codice:

Faccio notare come nel metodo dbout dobbiamo fare un’operazione un po’ superflua in apparenza, ovvero contare prima le righe e le colonne, per poi ridimensionare il vettore result. Questa operazione è necessaria perché ReDim, in VBA, non permette di ridimensionare l’intera matrice (vettore di vettori), ma solo l’ultima dimensione. Quindi, per esempio, result(4,10) potrebbe diventare result(4,20), ma non result(5,10) oppure result(5,20). Un’alternativa sarebbe quella di creare una funzione che trasponga il vettore e lo modifichi, ritrasponendolo di nuovo, ma dal punto di vista del calcolo credo sarebbe molto più oneroso che fare così.

Fatto questo potremo utilizzare la nostra nuova classe nella maniera seguente.

Una volta creata la classe può essere esportata, oppure importata nel modo seguente:

Per chi volesse è possibile scaricare la classe già pronta cliccando qui: DbSQL.zip

Hash MD5: A2A18A42BB11AEA86DF1AB4F547B4414

La classe è stata testata su Excel 2016.

Vedi articolo

Interrogare foglio di Excel con SQL in VBA

Obiettivo: interrogare e filtrare il contenuto di un foglio excel come se fosse una tabella di un database SQL

Prepariamo anzitutto un file Excel creando un foglio con dei dati come quelli di seguito:

Questo foglio lo chiamiamo DATI. Faccio notare che nella prima riga ho inserito le intestazioni della nostra tabella.

In un secondo foglio andiamo a creare un pulsante che richiamerà un form. L’idea sarebbe quella di creare un metodo di inserimento che guidi l’utente e gli permetta di filtrare una vasta base dati, eseguendo anche dei comandi di ricerca. Il risultato sarà il seguente:

Se proviamo a cercare qualcosa, per esempio “Mar” l’elenco dovrà essere filtrato e otterremo il risultato seguente:

Fatte queste premesse vediamo come realizzare l’interrogazione del foglio mediante VBA (sorvolo sull’elementare creazione del form).

Anzitutto creiamo una Sub che caricherà i valori nella combobox (cmbSeleziona) e nella listbox (lbSeleziona).

Faccio notare che in questo caso la query filtra solamente i valori in base al Nome, inoltre il parametro nome può essere passato in via opzionale, permettendo quindi anche di visualizzare tutti i campi se non c’è nessun nome da filtrare.

Ricordiamoci che per poter eseguire la connessione ADO abbiamo bisogno di importare l’opportuna libreria dai riferimenti.

A questo punto completiamo il nostro codice aggiungendo:

In questo modo ad ogni modifica (change) di txtCerca, che è la textbox di ricerca in cima al form, verrà interrogato il nostro foglio per produrre un risultato.

Il medesimo metodo LoadSelect, questa volta senza argomenti, lo aggiungiamo anche all’avvio del form, per caricare il contenuto completo senza filtri.

Qui è possibile scaricare il file creato con Excel 2016.

Vedi articolo

Calcolare IRR (Internal Rate of Return o TIR.COST) in VBA

Problema: la funzione TIR.COST (Tasso Interno di Rendimento) restituisce un unico valore per l’IRR anche quando ce ne è più di uno

Senza discutere sull’interpretazione dell’IRR stesso, immaginiamo di avere i seguenti due scenari di flusso di cassa.

I Scenario

Questo produce un grafico VAN-IRR come quello seguente:

In questo caso abbiamo due valori dell’IRR rispettivamente 15,47% e 33,33%; la funzione TIR.COST restituisce solamente il primo valore.

II Scenario

A questa serie di flussi di cassa corrisponde il seguente grafico:

In questo caso abbiamo due valori dell’IRR rispettivamente 1,99% e 19,65%; la funzione TIR.COST restituisce solamente il secondo valore.

Costruiamo quindi in VBA le funzioni necessarie per effettuare questo calcolo; come già mostrato anche qui utilizzeremo una funzione che restituisca un vettore di valori sotto forma di Variant, in modo da poterli valutare singolarmente (per esempio utilizzando le funzioni MIN e MAX, quando i valori sono solo due, quindi =MIN(IRRX(A1:A10)) oppure =MAX(IRRX(A1:A10))).

La funzione finale si chiama IRRX, quella che utilizzeremo dentro il foglio excel.

Le altre funzioni servono rispettivamente a:

  • IRRv1 – calcolare in modo approssimativo tutti gli IRR possibili su uno specifico flusso di cassa, con valore massimo per l’IRR al 30.000% (espresso come max = 300), inutile dire come questo valore possa essere impostato anche a molto meno per velocizzare i calcoli; l’IRR qui viene calcolato per ogni punto percentuale con scarti del 1%
  • IRRv2 – qui calcoliamo, per ogni valore di IRR trovato con la precedente funzione, il valore approssimato mediante iterazione su valori che vanno da IRRv2 ± 1%
  • VAN – questa funziona calcola semplicemente il VAN sul dato IRR

 

Testato su Excel 2016.

Vedi articolo

Creare una funzione personalizzata in VBA per Excel che restituisca una matrice

Obiettivo: creare una funzione personalizzata in Excel che restituisca come risultato una matrice n x m

Supponiamo di voler realizzare una funzione personalizzata in Excel che effettui la medesima operazione di MATR.PRODOTTO, ma allo stesso tempo consideri sempre la matrice in ingresso A come una matrice n x 1 e la matrice in ingresso B come una matrice 1 x m. Facciamo questa semplificazione a titolo di esempio.

Quindi abbiamo due matrici tipo queste:

A =  \begin{bmatrix}  a_1 \\ a_2 \\ a_3  \end{bmatrix}    B =  \begin{bmatrix}  b_1 & b_2 & b_3  \end{bmatrix}

Il loro prodotto risulterà così:

A \times B=  \begin{bmatrix}  a_1b_1 & a_1b_2 & a_1b_3 \\  a_2b_1 & a_2b_2 & a_2b_3 \\  a_3b_1 & a_3b_2 & a_3b_3  \end{bmatrix}

Nel nostro modulo aggiungiamo ora il seguente codice:

Notiamo che in questo modo vengono percorsi tutti i valori del range, indipendentemente dalla loro geometria, come se fossero in una riga o colonna.

Per testare il risultato possiamo fare una verifica su un esempio con la funzione MATR.SOMMA.PRODOTTO. Nel caso specifico creiamo una versione e parametrizzabile del SOMMA.PIÙ.SE.

A1:A7 e B1:B7 sono riempiti con valori da esempio. E2:H2 saranno quattro spazi dove potremo inserire, a nostro piacimento da 0 a 4 nominativi da verificare. In E3 abbiamo la seguente formula di controllo (NB: fatta con MATR.PRODOTTO):

=MATR.SOMMA.PRODOTTO(–(A2:A7=E2:H2);MATR.PRODOTTO(B2:B7;E1:H1))

In E4 aggiungiamo invece la formula con la nostra versione di MATR.PRODOTTO:

=MATR.SOMMA.PRODOTTO(–(A2:A7=E2:H2);MATH_MATRICE(B2:B7;E1:H1))

Scopo raggiunto.

Vedi articolo