martes, 30 de agosto de 2016

Curso Excel Presencial en Madrid - 20 horas - Nivel Avanzado

¿Vives en Madrid?, ¿Te interesa la mejor formación en Excel presencial?.
Curso Presencial Excel Avanzado en Madrid con una duración de 20 horas...

INICIO: lunes 26 de septiembre de 2016
PRECIO: 249,00 euros
FECHAS: Lunes 26 Septiembre 2016 a Viernes 7 Octubre 2016.
HORARIO: 19:00 - 21:00 (de lunes a viernes)
PROFESOR: Ismael Romero (Experto en Excel y Microsoft MVP Excel 2014,2015 y 2016)
LUGAR: C/ Marqués de Ahumada, 7 28.028 Madrid
-----------------------PLAZAS LIMITADAS!!!---------------------------------
MATRÍCULA: Hasta el viernes 16 de septiembre de 2016.

OBJETIVOS: Dominar las herramientas y utilidades avanzadas de Excel, tales como insertar y trabajar con diferentes tipos de datos, crear fórmulas y funciones, matriciales, trabajar con rangos, nombres definidos, insertar tablas, trabajar con bases de datos, tablas dinámicas..

¡¡¡Plazas limitadas a 10 alumnos!!!

Infórmate en
cursos@excelforo.com o aquí
Excel Avanzado Presencial Madrid.


... y por supuesto la mejor formación elearning(online).
Haz de Excel tu mejor aliado!
Aprende con los mejores y adquiere una buena base: Edición de Cursos de Excel y Macros online con tutor personal de Septiembre de 2015.
Nunca estudiar fue tan fácil.


Los cursos de Excel y Macros abiertos para este mes de Septiembre son:

Curso Excel Avanzado

(ver más)

Curso Macros Medio

(ver más)



Curso Macros Iniciación

(ver más)

Curso Excel Nivel Medio

(ver más)


Curso Tablas dinámicas en Excel

(ver más)

Curso preparación MOS Excel 2010 (Examen 77-882)

(ver más)


Curso Excel Financiero

(ver más)



Esta nueva edición de Cursos de Excel y macros en modalidad elearning (online) comienzan el día 1 de Septiembre de 2016; y la matrícula estará abierta hasta el día 10.

Excelforo: con la confianza de siempre....estás a tiempo!!

También formación Excel a empresas. Explota los recursos a tu alcance (ver más).


Informarte sin compromiso en cursos@excelforo.com o directamente en www.excelforo.com.

jueves, 25 de agosto de 2016

Solver y el método de multiplicación egipcio

En la entrada del blog anterior vimos/aprendimos el método ruso de multiplicación.
Hoy toca el método egipcio sobre el que se basaba aquel.

Este antiguo método egipcio, donde sólo se requiere saber sumar consiste en que para multiplicar A x B:
1-En la primera columna se escribe la serie: f(n)=2n , partiendo desde n=0 continuando mientras 2n <A. Los primeros números de la serie quedarían de la siguiente manera: 1,2,4,8...
2-En la segunda columna se escribe la serie: f(n)=2nxB, o bien f(n)=2xf(n-1) siendo f(0)=B. El resultado es el mismo y obtendremos la siguiente serie: B, 2xB, 4xB...
3-En una tercera columna se marcan las cifras de la primera columna cuya suma resulte igual a A (de mayor a menor)
4-El resultado es la suma de las cifras marcadas de la segunda columna.

Puedes leer algo más en Wikipedia


Partimos de la siguiente plantilla:

Solver y el método de multiplicación egipcio



La disposición de las fórmulas en nuestro modelo.
En C2 y en D2 el primer y segundo valor de nuestro producto.

En B4:B15 la primera parte del cálculo, cumpliendo la condición dada, para lo que insertamos la fórmula:
=SI(POTENCIA(2;A4)<$D$2;POTENCIA(2;A4);"")

En C4:C15 incorporamos como primer valor en C4 el segundo importe a multiplicar (=313) y en C5 y siguientes multiplicamos por dos el anterior:
=+C4*2


En el rango D4:D15 no añadimos nada, ya que es en este rango donde trabajará Solver.
Sí añadimos en D16 la fórmula:
=SUMAPRODUCTO(B4:B15;D4:D15)
que será la celda objetivo de nuestra herramienta Solver.
Con Solver conseguiremos que este rango D4:D15 se complete con valores 0 y 1 hasta que D16 sume el valor del segundo importe (=313).Con lo que cumpliríamos el tercer paso de los indicados al inicio del post.

Antes de configurar Solver terminamos nuestra plantilla, rellenando el rango E4:E15 con el producto:
=C4*D4
sumando todo ello en la celda E16.
Este resultado final corresponde a la suma de los importes de la segunda columna para aquellas cantidades de la primera columna cuya suma resulte igual al primer importe.


Con el método claro (más o menos) y la plantilla montada, estamos listos para configurar Solver; así pues accedemos a la Ficha Datos > Análisis > Solver:

Solver y el método de multiplicación egipcio



De manera muy sencilla definimos como celda objetivo la celda D16, con el valor 313 (el importe del segundo importe del producto).
Las celdas cambiantes que deseamos se modifiquen serán las del rango D4:D15.
Definiendo la restricción de este mismo rango D4:D15 como números binarios.


Al resolver nuestra configuración obtendremos la confirmación:

Solver y el método de multiplicación egipcio


Y el resultado plasmado en la hoja de cálculo, con el que conseguimos el producto de 13 x 313 buscado:

Solver y el método de multiplicación egipcio



Notemos como los valores de la primera columna que corresponden con los valores 1 del rango D4:D15 suman los 313 necesarios(=256+32+16+8+1)... cumpliendo la tercera condición del método.

martes, 23 de agosto de 2016

VBA: Multiplicación por duplicación - la multiplicación rusa.

Quizá este método de multiplicación rusa (o por duplicación) te suene a 'chino'...
pero es tan antiguo como la cultura de los faraones.
Es un método de multiplicación basado en la suma y división por dos.
Puedes echar un vistazo en Wikipedia.

Pero se resume básicamente en:
1-Escribir los números (A y B) que se desea multiplicar en la parte superior de sendas columnas.
2-Dividir A entre 2, sucesivamente, ignorando el resto, hasta llegar a la unidad. Escribir los resultados en la columna A.
3-Multiplicar B por 2 tantas veces como veces se ha dividido A entre 2. Escribir los resultados sucesivos en la columna B.
4-Sumar todos los números de la columna B que estén al lado de un número impar de la columna A. Éste es el resultado.


Veamos un ejemplo del desarrollo en Excel.
Tenemos dos valores a multiplicar en C2:=13 y en D2:=313
En el primer rango C5:C14 hemos añadido el valor de C2 (=13) y los siguientes (C6 y siguientes) la fórmula:
=ENTERO(C5/2)
con la que conseguimos los valores enteros necesarios.


En la columna de al lado, rango D5:D14, completamos con:
En D5 recuperamos el valor de D2 (=313), y en D6 y siguientes añadimos la fórmula:
=SI(C6>0;D5*2;0)
obteniendo el doble del valor anterior...


Finalmente en el rango E5:E14 incorporamos el condicional:
=SI(ES.IMPAR(C5);D5;0)
que nos retornará los importes necesarios a sumar, sólo para los valores impares del primer rango(C5:C14).
Acabamos sumando en E15 los importes del rango superior:
=SUMA(E5:E14)

VBA: Multiplicación por duplicación - la multiplicación rusa.



Obviamente, por suerte, no tendremos que hacer estas operaciones tan laboriosas para conseguir nuestro producto... pero sirve de introducción para el ejercicio siguiente, que consiste en replicar el método en VBA para Excel.
Construiremos una UDF, un procedimiento Function, como el que sigue:

Function MultRusa(pdto1 As Integer, pdto2 As Double) As Double
'definimos dos matrices
Dim Valores1() As Variant
Dim Valores2() As Variant
'calculamos el número de lineas a trabajar
N = 0
Do
    'tantas lineas necesarias
    'hasta que superemso el valor del primer valor a multiplicar
    Eltos = 2 ^ N
    N = N + 1
Loop Until Eltos > pdto1

'redefinimos la dimensión de nuestras matrices
'ajustadas al númer de líneas necesarias
ReDim Valores1(1 To N) As Variant
ReDim Valores2(1 To N) As Variant

'asignamos valores a las matrices
Valores1(1) = Int(pdto1)    'el primer rango será siempre entero
Valores2(1) = pdto2
'completamos las matrices con el siguiente bucle
N = 2
Do
    'a partir del valor anterior calculamos el siguiente dividiendo por dos
    Valores1(N) = Int(Valores1(N - 1) / 2)
    'a partir del valor anterior calculamos el siguiente multiplicando por dos
    Valores2(N) = Valores2(N - 1) * 2
    N = N + 1
Loop Until Valores1(N - 1) = 1

'recorremos las matrices
Dim dato As Variant
For Each dato In Valores1
    x = x + 1
    If (dato Mod 2) <> 0 Then
        'acumulamos los importes de la segunda matriz
        'solo cuando el valor de la primera sea impar
        Suma = Suma + Valores2(x)
    End If
Next dato

'devolvemos el sumando a la celda
MultRusa = Suma
End Function



El resultado, claro está, es el esperado:

VBA: Multiplicación por duplicación - la multiplicación rusa.



Lo interesante de este post, me atrevo a decir, es el uso de las Arrays y de los loops tipo DO...LOOP y FOR EACH..NEXT, empleados para la construcción de los rangos necesarios (divididos y multiplicados por dos), así como para obtener el número de líneas en esos rangos....

jueves, 18 de agosto de 2016

VBA: Cuándo y Cómo pasar argumentos por ByVal o por ByRef

Si llevas un tiempo programando, seguro que te ha surgido la duda en determinados momentos de cómo y cuándo emplear y definir tus argumentos (de procedimientos Sub o Function) como ByVal o como ByRef.
Trataré hoy de aclarar este aspecto importante para el comportamiento de nuestras macros.


Lo primero a indicar es que la omisión, esto es, por defecto, en VBA para Excel, se pasan los argumentos Por Referencia (ByRef). (OJO por que lo normal en otros lenguajes es que sea por valor!).

1 - ByVal - "Por Valor":
Si tenemos distintos procedimientos (Sub y/o Function) y en todos ellos empleamos la misma variable ByVal, entonces tomamos un valor en un primer procedimiento y cuando este valor cambie en otro procedimiento, sólo cambiará en el segundo y no en el primero.
Dicho de otro modo, pasamos un valor numérico, este número se copia y se usa, por ejemplo, en una función.... si cambiamos el número, se cambiará en la función.

2 - ByRef - "Por Referencia":
Si tenemos distintos procedimientos (Sub y/o Function) y en ambos se emplea la misma variable, con ByRef toma un valor en un primer proceso, si este valor cambia en el segundo, cambiará también en el primero.
Las referencias son instancias de la misma variable; es como la misma variable usada en muchos lugares. Por tanto si la modificamos en la función, lo hará en el resto de nuestra programación.


Hablemos de cuándo emplear uno y otro...
La ventaja de pasar un argumento con ByRef es que el procedimiento puede devolver un valor al código de llamada por medio del argumento.
La ventaja de pasarlo con ByVal es que protege a la variable de los cambios que sobre ella pueda efectuar el procedimiento.

Aunque el mecanismo que usemos para pasar argumentos afecta al rendimiento de nuestras macros, la diferencia es insignificante. Una excepción es cuando se pasa un tipo de valor grande con ByVal (copia todo el contenido de los datos del argumento), en cuyo caso, lo más eficiente es pasarlo como ByRef.

En definitiva, cuando el elemento del código de llamada subyacente al argumento es un elemento no modificable, declararemos el parámetro correspondiente ByVal, ya que ningún código podrá cambiar el valor de un elemento no modificable.
Por contra, si el procedimiento necesita realmente modificar el valor subyacente en el código de llamada, declararemos el parámetro correspondiente ByRef.


Mejor vemos el comportamiento con un ejemplo.
Plantearemos un par de procedimientos iguales, con la única diferencia que en el primer caso se pasará como ByVal y en el segundo como ByRef.


Primer caso: Pasando un argumento Por Referencia (ByRef).

Sub PorReferencia()
Dim dato As Integer
dato = 13

Debug.Print "Dato pto1:=" & dato
'Pasamos la variable Por Valor (ByRef) con valor 13
'a través del procedimeinto SumarPorValor
Call SumarPorReferencia(dato)

'Muestra que el valor= 1013
'ya que se modificó al usar la función SumarPorReferencia
Debug.Print "Dato pto3:=" & dato

End Sub
Sub SumarPorReferencia(ByRef Valor As Integer)
'Modifica la variable
Valor = Valor + 1000
Debug.Print "Dato pto2:=" & Valor
End Sub


Al ejecutar el procedimiento 'PorReferencia' observamos en la ventana de Inmediato las tres etapas por las que pasa la variable:
Dato pto1:=13
Dato pto2:=1013
Dato pto3:=1013

Es decir, observamos la transformación del dato.. al cambiar en el segundo procedimiento 'SumarPorReferencia' lo modifica en el primero...


Veamos la diferencia ahora si empleamos y pasamos la variable Por Valor (ByVal).

Sub PorValor()
Dim dato As Integer
dato = 13

Debug.Print "Dato pto1:=" & dato
'Pasamos la variable Por Valor (ByVal) con valor 13
'a través del procedimeinto SumarPorValor
Call SumarPorValor(dato)

'Muestra que el valor= 13
'no se modificó al usar la función SumarPorValor
Debug.Print "Dato pto3:=" & dato

End Sub
Sub SumarPorValor(ByVal Valor As Long)
'Modifica la variable
Valor = Valor + 1000
Debug.Print "Dato pto2:=" & Valor
End Sub



Al ejecutar el procedimiento 'PorValor' observamos en la ventana de Inmediato las tres etapas por las que pasa la variable:
Dato pto1:=13
Dato pto2:=1013
Dato pto3:=13

Es decir, observamos la NO transformación del dato.. al cambiar en el segundo procedimiento 'SumarPorValor' NO se modifica en el primero...


Un último ejemplo...

Sub PasandoArgumentos()
Dim Valor1 As Integer, Valor2 As Integer

'Damos valores a las variables
Valor1 = 888: Valor2 = 999        'por defecto ambos pasados

Debug.Print "Antes = Valor1: " & CStr(Valor1), "Valor2: " & CStr(Valor2)

'cambiamos el modo en que pasamos los argumentos
Call Cambio(X:=Valor1, Y:=Valor2)
Debug.Print "Después =  Valor1: " & CStr(Valor1), "Valor2: " & CStr(Valor2)
End Sub

Sub Cambio(ByRef X As Integer, ByVal Y As Integer)
'valores después de la llamada
X = 222     'pasado por referencia
Y = 444     'pasado por valor
End Sub



Aquí se observa, en la ventana de inmediato:
Antes = Valor1: 888 Valor2: 999
Después = Valor1: 222 Valor2: 999
como la variable que se ha pasado como valor (ByVal) no cambia (=999)... mientras que la que pasamos como referencia toma el valor modificado por el segundo procedimiento (de 111 a 222).

martes, 16 de agosto de 2016

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel

Toca hoy hablar de dos complementos que vemos en nuestro explorador de proyectos de VBA al entrar en el Editor de VB: FUNCRES.XLAM y ATPVBAEN.XLAM

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel



¿Qué son y por qué aparecen?.
Estos complementos/proyectos están directamente relacionados con los complementos de Herramientas de Análisis (en inglés Analysis ToolPak - o ATP).
Cuando hemos decidido que, por necesidad, queremos instalar estos complementos, éstos (.xlam) aparecerán en nuestro Explorador de proyectos VB.
Los complementos:

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel



Estos dos complementos los localizamos instalados físicamente en la siguiente ruta (para 2013):
C:\Program Files (x86)\Microsoft Office\Office15\Library\Analysis
cambia la parte de la ruta \Office15\ por tu versión...
Si accedemos veremos los siguientes complementos:
ANALYS32.XLL
ATPVBAEN.XLAM
ATPVBAES.XLAM
FUNCRES.XLAM
PROCDB.XLAM

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel



Recomendación: para versiones superiores a 2007 no es necesario instalar el complemento 'Herramienta de Análisis, ya que está instalado por defecto y el estándar ya contiene las funciones extra... Así solo instalaremos si es necesario el complemento de 'Herramientas de Análisis - VBA'.

El mecanismo y relación entre todos estos archivos es 'simple', el proyecto ATPVBAEN.XLAM da acceso a la librería de Excel ANALYS32.XLL. Finalmente FUNCRES.XLAM contiene un módulo de VBA con un 'RibbonX_Code' con programación asociada con el grupo Análisis en la Ficha Datos de la cinta de opciones:

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel


Como curiosidad, normalmente todos los complementos instalados están ocultos y protegidos con contraseña.. pero si tratas de entrar al proyecto FUNCRES.XLAM comprobarás y verás con sorpresa que Microsoft ha olvidado protegerlo!, y por tanto el módulo es visible...
En todo caso, mejor ignóralo, y desde luego no toques nada!!.
Este es el código que verías:

'Entry point for RibbonX button click
Sub ShowATPDialog(control As IRibbonControl)
    Application.Run ("fDialog")
End Sub

'Callback for RibbonX button label
Sub GetATPLabel(control As IRibbonControl, ByRef label)
    label = ThisWorkbook.Sheets("RES").Range("A10").Value
End Sub



Lo que aporta instalar estos complementos de Análisis son una serie de funciones extra, asi como unas cuantas funcionalidades y herramientas específicas (tanto para la hoja de cálculo como usar dentro de nuestras macros en VBA).

Una recomendación general de los complementos (y en particular para el ATPVBAEN.XLAM), si no lo vas a emplear, mejor no lo instales... ya que el problema de estos complementos es que requiere y consume recursos en el momento de apertura de cualquier Libro (Nuevo o existente)...


Mencionar, por último, que de manera adicional es posible instalar en modo de 'Referencia' este complemento. Lo haríamos desde el editor de VB > menú Herramientas > Referencias:

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel


Al habilitar la referencia veríamos en nuestro explorador de proyectos:

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel


Trabajar de este modo nos permitiría trabajar sin prefijo al usar las funciones en nuestras macros.

VBA: Complementos FUNCRES.XLAM y ATPVBAEN.XLAM de Excel



Un poco de información de Microsoft adicional (algo vieja pero útil):
https://support.microsoft.com/es-es/kb/192642

jueves, 11 de agosto de 2016

VBA: Los Módulos de Clase en Excel (Class Modules) - parte II.

Al hilo de la entrada anterior (ver) plantearemos hoy un ejemplo de uso concreto de los módulos de clase.

Para el ejemplo implantaremos el cálculo del área y perímetro de un triángulo.

Supongamos tenemos varios elementos/datos de triángulo, de los cuales necesitamos calcular el área y el perímetro:

VBA: Los Módulos de Clase en Excel (Class Modules) - parte II.



La idea será practicar con una clase personalizada (un módulo de clase) que llamaremos: Triangulo_Class:
Siguiendo las indicaciones del post del día anterior, generamos y definimos nuestras variables, las propiedades Get y Let que necesitaremos, y por último generaremos un par de métodos Function.
El código añadido dentro de nuestro módulo de clase es:

'Variables privadas
Private pLado1 As Double
Private pLado2 As Double
Private pLado3 As Double
Private pAltura As Double
''''''''''''''''''''''''''''''''''''''''''''''''''
Public Property Let lado1(valor As Double)
pLado1 = valor
End Property
Public Property Get lado1() As Double
lado1 = pLado1
End Property
'''''''
Public Property Let lado2(valor As Double)
pLado2 = valor
End Property
Public Property Get lado2() As Double
lado2 = pLado2
End Property
''''''''''
Public Property Let lado3(valor As Double)
pLado3 = valor
End Property
Public Property Get lado3() As Double
lado3 = pLado3
End Property
''''''''''
Public Property Let altura(valor As Double)
pAltura = valor
End Property
Public Property Get altura() As Double
altura = pAltura
End Property

'Propiedades de lectura....
Property Get SemiPerimetro() As Double
'calculamos el semiperímetro
SemiPerimetro = (pLado1 + pLado2 + pLado3) / 2
End Property

Property Get Area1() As Double
Area1 = (SemiPerimetro * (SemiPerimetro - pLado1) * (SemiPerimetro - pLado2) * (SemiPerimetro - pLado3)) ^ (1 / 2)
End Property

Property Get Area2() As Double
Area2 = (pLado1 * pAltura) / 2
End Property

Property Get Perimetro() As Double
Perimetro = pLado1 + pLado2 + pLado3
End Property

'métodos---------------------------------------------------------------------------
Function CalculoArea(l_1 As Double, l_2 As Double, l_3 As Double, Optional h As Double) As Double
Dim oTriangulo As Triangulo_Class
Set oTriangulo = New Triangulo_Class
oTriangulo.lado1 = l_1
oTriangulo.lado2 = l_2
oTriangulo.lado3 = l_3
oTriangulo.altura = h

If h <> 0 Then
    'si informamos de la altura
    'calculamos b x h /2
    CalculoArea = oTriangulo.Area2
Else
    'en caso de NO informar altura
    'calculamos con Semiperimetro
    CalculoArea = oTriangulo.Area1
End If

Set oTriangulo = Nothing
End Function
Function CalculoPerimetro(l_1 As Double, l_2 As Double, l_3 As Double) As Double
Dim oTriangulo As Triangulo_Class
Set oTriangulo = New Triangulo_Class
oTriangulo.lado1 = l_1
oTriangulo.lado2 = l_2
oTriangulo.lado3 = l_3

CalculoPerimetro = oTriangulo.Perimetro

Set oTriangulo = Nothing
End Function



Para interactuar en la hoja de cálculo directamente creamos e insertamos otras funciones personalizadas (UDF) en un módulo estándar:

Function CalcArea(l1 As Double, l2 As Double, l3 As Double, Optional h As Double)
Dim oTriangulo As Triangulo_Class
Set oTriangulo = New Triangulo_Class

CalcArea = oTriangulo.CalculoArea(l1, l2, l3, h)

Set oTriangulo = Nothing
End Function
''''''''''''''''''''''''''''''''''''''
Function CalcPerimetro(l1 As Double, l2 As Double, l3 As Double)
Dim oTriangulo As Triangulo_Class
Set oTriangulo = New Triangulo_Class

CalcPerimetro = oTriangulo.CalculoPerimetro(l1, l2, l3)

Set oTriangulo = Nothing
End Function



Son precisamente estas funciones del módulo estándar las que empleamos en las celdas de la hoja de cálculo.

El resumen de lo expuesto es que, tras definir las variables a emplear (tres lados y altura), hemos definido sus propiedades Get y Let para cada una de ellas, que nos permiten recuperar y añadir valor a estas variables.
Además hemos incorporado una serie de propiedades solo de lectura (añadiendo únicamente la propiedad Get):
SemiPerimetro
Area1
Area2
Perimetro

Para finalmente construir unas funciones (métodos) que trabajando sobre las propiedades y variables anteriores, nos permiten recuperar los valores deseados: Área y Perímetro.

Todo esto en un módulo de clase.


El trabajo finaliza al llamar a estos métodos desde otras funciones UDF dentro un módulo estándar:
'CalcArea' y 'CalcPerimetro'.


Como comentábamos en el post del día anterior, por supuesto, es posible hacer lo mismo sin emplear las 'Clases', pero hacerlo simplifica y facilita nuestros procedimientos y macros en Excel.

martes, 9 de agosto de 2016

VBA: Los Módulos de Clase en Excel (Class Modules) - parte I.

Abordaremos hoy un punto importante para los amantes de la programación en VBA para Excel... y que tengan ya un nivel algo avanzado: Los Módulos de Clase (Class Modules).

Sabemos que el lenguaje y la programación en VBA para Exel está estructurada y orientada a objetos OOPL (Object Oriented Programming Language).. piensa durante un segundo todos los objetos habituales con los que trabajas al programar en VBA para Excel (objeto Workbook, Sheets, Range, Chart, y un largo etcétera).
Sin duda alguna para pequeñas y/o grandes cosas empleamos el VBA 'normal' empleando los objetos ya definidos por las distintas librerías y referencias que existen en Excel, y nos basta emplear los módulos de programación estándar, así como otras ventanas de código.
Ahora bien, existen situaciones más complicadas donde esto no es suficiente, donde realizar un proceso con programación estructurada (la estándar de toda la vida), necesitaríamos una vida y muchas líneas de código que recogieran todas las posibilidades... o en un escenario peor, donde no tuviéramos cerrado el número de variables... es aquí donde el uso de las 'Clases' nos aporta fortaleza a nuestra programación, evitando de paso, posibles fallos y errores de programación.

Parece claro que la posibilidad de programar características o propiedades genéricas de un objeto, para luego utilizar estas propiedades reducirá la cantidad de código significativamente, así como ganar en claridad (ventajas fundamentales).

En definitiva, emplear las clases (propiedades, métodos y eventos) propias, generadas y definidas por nosotros, es una forma de optimizar nuestras programaciones en VBA.

Tres puntos básicos que comparan el uso del módulo estándar frente al módulo de clase:
1-En un módulo estándar sólo hay una copia de los datos ; sin embargo, en un módulo de clase se crea por separado para cada instancia de la clase. De ahí que cada instancia de la clase tiene su propio conjunto de variables de clase individual al objeto.
2-Los datos, en un módulo estándar, existen tanto como su ámbito de aplicación del programa - el proyecto de VBA. Sólo cuando se cierra el archivo se pierden los datos de todas las variables globales. En un módulo de clase, sin embargo, existen datos para cada instancia sólo durante el tiempo de vida del objeto. Deja de existir cuando la clase de objeto es destruido; por ejemplo, cuando el objeto de clase se establece en Nothing (Set clase = Nothing).
3-Las variables declaradas pública en un módulo estándar son visibles en cualquier parte de su proyecto. Sin embargo, las variables públicas en un módulo de clase son accesibles únicamente si tiene una variable de objeto que contiene una referencia a una instancia particular de esa clase.


Entrando en materia, es muy importante conocer que en un objeto que vayamos a crear existen y podemos trabajar con:
1-Propiedades: Serían las características concretas que se pueden definir o parametrizar. Emplearemos la propiedad Get, la propiedad Let y Set:
a)Get := devuelve/extrae el valor del objeto.
b)Let := inserta el valor dentro del objeto.
c)Set := si aplica, configura el objeto. Establecer el valor de una propiedad que contiene una referencia a un objeto

2-Métodos: Se refiere a las conductas o acciones que puede realizar. Una vez claras las propiedades, agregamos los métodos (procedimientos ordinarios, que trabajarán con los datos del objeto).

3-Eventos: Se refiere a eventos externos que desencadenan acciones de manera interna. Son básicamente dos: Class_Initialize() y Class_Terminate().


Para asentar tanta 'palabrería' propongo generar una Clase a modo de ejemplo: La Clase SmartPhone
Desde el Editor de VBA seleccionamos Insertar > Módulo de clase.

VBA: Los Módulos de Clase en Excel (Class Modules) - parte I.


Es importante tener en cuenta que:
* todo objeto personalizado (toda clase) debe de tener su propio módulo.
* el módulo de clase es necesario renombrarlo para reflejar el objeto personalizado!!.


Ahora que tenemos creado nuestro módulo de clase, definiremos cuales son las propiedades y los métodos que necesitamos.
Tres características que definen de manera concreta un SmartPhone (todas las que necesitemos):
1- Marca
2- Modelo
3- Memoria

Estas variables serán la que almacenarán la información de propiedades internamente dentro del módulo. Optamos por variables privadas, ya que no queremos que desde afuera sean accesible (podríamos haber optado por definirlas como Public según necesidades).
Private pMarca As String
Private pModelo As String
Private pMemoria As Double


No importa que las hayamos definido como Private, ya que para poder emplearlas desde cualquier lugar de nuestro proyecto, usaremos las propiedades Get y Let (que son Públicas).


'''''''''''''''''''''''''''''''''''''''''''''
'Propiedad Marca
Public Property Get Marca() As String
    Marca = pMarca
End Property
Public Property Let Marca(Valor As String)
    pMarca = Valor
End Property

'''''''''''''''''''''''''''''''''''''''''''''
' Propiedad Modelo
Public Property Get Modelo() As String
    Modelo = pModelo
End Property
Public Property Let Modelo(Valor As String)
    pModelo = Valor
End Property

'''''''''''''''''''''''''''''''''''''''''''''
' Propiedad Memoria
Public Property Get Memoria() As Double
    Memoria = pMemoria
End Property
Public Property Let Memoria(Valor As Double)
    'controlamos que se introduce un número
    If IsNumeric(Valor) Then
        'y además con ABS lo gestionamos siempre como positivo
        pMemoria = Abs(Valor)
    Else
        'si no fuera un número, forzamos el valro como cero
        pMemoria = 0
    End If
End Property


Como puedes observar, se puede incorporar código para validación y manejo de errores en los procedimientos que reciben valores o envían valores desde el objeto que has creado (Get y Let).
Además, es posible crear sólo el código para el procedimiento Property Get (omitiendo el de Property Let), teniendo entonces una propiedad que es sólo de lectura.
'''''''''''''''''''''''''''''''''''''''''''''
' Propiedad solo lectura: MemoriaLibre
Public Property Get MemoriaLibre() As Double
    MemoriaLibre = pMemoria * 0.85
End Property



Finalmente (de momento) podríamos definir dentro de nuestro módulo de clase, además de las propiedades anteriores, métodos que son procedimientos Sub que realizan una acción u operación; y/o procedimientos Function que devuelven un valor después de realizar una operación.
Un planteamiento de métodos de nuestra clase podría ser:
Public Sub Llamada()
    'código aquí
    MsgBox "Hemos realizado una llamada"
End Sub
 
Public Sub SMS()
    'código aquí
    MsgBox "Acabas de enviar un SMS"
End Sub
 
Public Sub Datos()
    'código aquí
    MsgBox "Estás consumiento datos"
End Sub



Estos métodos representan tres acciones posibles que podemos realizar con nuestro objeto(SmartPhone)...

Acabamos hoy con un simple ejemplo de cómo poder hacer uso de nuestra recién creada clase.
Para crear un objeto de nuestra clase 'SmartPhone_Class', haríamos lo siguiente:
Dim Smart1 As New SmartPhone_Class


Otra posibilidad es primero declarar la clase y luego asignar un objeto:
Dim Samrt1 As SmartPhone_Class
Set Smart1 = New SmartPhone_Class


Y podríamos 'inicializar' sus propiedades así:
Smart1.Marca = "LG"
Smart1.Modelo = "G4"
Smart1.Memoria = "8"


Para usar los métodos declarados:
Smart1.Llamada
Smart1.SMS
Smart1.Datos



En un módulo estándar del proyecto insertamos lo siguiente:

Sub Ejemplo1()
Dim Smart1 As New SmartPhone_Class
'resto de código
End Sub

Sub Ejemplo2()
Dim Samrt1 As SmartPhone_Class
Set Smart1 = New SmartPhone_Class
'resto de código

Set Smart1 = Nothing
End Sub

Sub Ejemplo3()
Dim Samrt1 As SmartPhone_Class
Set Smart1 = New SmartPhone_Class

'asignamos valores a las variables...
Smart1.Marca = "LG"
Smart1.Modelo = "G4"
Smart1.Memoria = "8"

Set Smart1 = Nothing
End Sub

Sub ejemplo4()
Dim Samrt1 As SmartPhone_Class
Set Smart1 = New SmartPhone_Class

'lanzará los métodos creados...
Smart1.Llamada
Smart1.SMS
Smart1.Datos

Set Smart1 = Nothing
End Sub



En el siguiente post mostraré un ejemplo completo del uso de los módulos de clase.

jueves, 4 de agosto de 2016

Obtener posición dentro de un rango según condición

Un lector preguntaba por la manera de obtener, mediante fórmulas, la posición según orden ascendente de diferentes valores, condicionados a un segundo elemento:



Observemos la fórmula matricial empleada para cada celda del rango C2:C18 (recuerda ejecutarla presionando Ctrl+Mayusc+Enter):
=COINCIDIR(B2;K.ESIMO.MENOR(SI($A$2:$A$18=A2;$B$2:$B$18);FILA($A$2:$A$18)-1);0)


¿Cómo funciona esta matricial?.

Empecemos con lo primero importante que es la condición del 'Producto':
SI($A$2:$A$18=A2;$B$2:$B$18)
matricialmente ejecutada esto nos devuelve la siguiente matriz:
{10000;20000;30000;50000;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO;FALSO}
así solo trabajaremos sobre los valores correspondientes al producto que nos interesa.


Al aplicar la función K.ESIMO.MENOR sobre estos valores obtendremos los datos ordenados de menor a mayor (en sentido ascendente):
K.ESIMO.MENOR(SI($A$2:$A$18=A2;$B$2:$B$18);FILA($A$2:$A$18)-1)
la matriz modificada:
{10000;20000;30000;50000;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!;#¡NUM!}


Finalmente con COINCIDIR del valor de la fila sobre el rango ordenado obtenemos la posición absoluta que ocupa este dato.

Como se puede probar da igual el orden en que aparezcan los valores, esta fórmula trabajará siempre correctamente.

Obtener posición dentro de un rango según condición

martes, 2 de agosto de 2016

Separar valor numérico de unidad de medida de un texto

En alguna ocasión, más de las que me gustaría haber visto, me he encontrado con usuarios que a la hora de introducir registros de información, insertan en una celda el valor numérico seguido por la unidad de medida...
Algo así:
850,50 Kg

El problema que genera esto es obvio... ese dato es inoperable al tratarlo Excel como un texto.


La cuestión de hoy será cómo recuperar el valor numérico de esa celda.
Mostraremos en este post tres maneras distintas de conseguir el dato.



Posiblemente la fórmula más simple sea:
=VALOR(IZQUIERDA(A1;ENCONTRAR(" ";A1)))

donde trabajamos con funciones típicas de texto como IZQUIERDA, ENCONTRAR y la útil VALOR.
Con ENCONTRAR localizamos el espacio que separa el valor numérico de la unidad de medida, y a partir de ese punto, con IZQUIERDA recuperamos los caracteres numéricos.. finalmente con VALOR convertimos en número ese 'texto numérico'


Otra función, algo más elaborada y matricial! sería:
=MAX(SI.ERROR(--IZQUIERDA(A1;FILA(INDIRECTO("1:"&LARGO(A1))));0))

la clave es la parte profunda de la fórmula:
--IZQUIERDA(A1;FILA(INDIRECTO("1:"&LARGO(A1))))
con la que, matricialmente, llegamos a esto:
{8;85;850;850;850,5;850,5;850,5;#¡VALOR!;#¡VALOR!}
es decir, repasa 'in crescendo' todos los caracteres de la celda, uno por uno.
Con los dos signos negativos antes de IZQUIERDA forzamos lo lea como número cuando así lo sea, en caso contrario tendríamos el error de #¡VALOR! (como se ve en el matriz de constantes anterior).
Este error lo tratamos con la función SI.ERROR:
SI.ERROR(--IZQUIERDA(A1;FILA(INDIRECTO("1:"&LARGO(A1))));0)
que devuelve la matriz corregida:
{8;85;850;850;850,5;850,5;850,5;0;0}
para finalmente aplicarle la función MAX para retornar el valor buscado.


Una alternativa más (y seguro hay muchas más):
=-BUSCAR(1;-IZQUIERDA(A1;FILA(INDIRECTO("1:"&LARGO(A1)))))

similar a la anterior, ya que con la misma estructura
-IZQUIERDA(A1;FILA(INDIRECTO("1:"&LARGO(A1))))
conseguimos la matriz de constantes:
{-8;-85;-850;-850;-850,5;-850,5;-850,5;#¡VALOR!;#¡VALOR!}

Sobre esta matriz NO ordenada en sentido ascendente (tal como requiere el uso de esta función BUSCAR), buscar el elemento 1 (en realidad cualquier valor), tiene el efecto de devolver el último de los valores, esto es, el valor numérico que buscábamos.