Esta nota trata sobre cómo extraer valores únicos de rangos que contienen más de una columna o rangos discontinuos.
Excel tiene dos métodos incorporados para esta tarea. En Excel 2007-2013 ambos se encuentran en la pestaña Datos: Filtro Avanzado y Quitar duplicados
Filtro Avanzado nos permite hacerlo con relativa facilidad usando la posibilidad, como mostramos en este video:
Con Quitar duplicados la técnica es un poco más elaborada, ya que incluye copiar la lista a un rango apartado y allí extraer los duplicados (si estamos interesados en guardar la lista original)
Las limitaciones de estos métodos comienzan cuando queremos extraer valores únicos de rangos discontinuos o de rangos que contienen más de una columna.
Podemos hacerlo con un código relativamente sencillo, similar al que mostramos en la nota sobre listas desplegables dependientes publicada hace poco. Este código se basa en el objeto Collection. El código es el siguiente
Sub extraerUnicos_Hoja()
'extraer valores unicos de rangos de varias columnas o no continuos
'Jorge Dunkelman - JLD Excel Blog, mayo 2013
Dim collUnicos As New Collection
Dim vcollItem As Variant
Dim rngCell As Range, rngDatos As Range, rngLista As Range
Dim lCounter As Long
Set rngDatos = Application.InputBox(prompt:="Seleccione rango/s con datos", Type:=8)
Set rngLista = Application.InputBox(prompt:="Seleccione la primera celda de la lista", Type:=8)
On Error Resume Next
For Each rngCell In rngDatos
collUnicos.Add rngCell, Cstr(rngCell)
Next rngCell
On Error GoTo 0
lCounter = 0
For Each vcollItem In collUnicos
rngLista.Offset(lCounter, 0) = CStr(vcollItem)
lCounter = lCounter + 1
Next vcollItem
End Sub
Con este código definimos el rango que contiene los datos (que puede contener varias columnas o ser discontinuo, pero todos los datos deben estar en la misma hoja), definimos la celda desde donde queremos empezar a pegar la lista de registros únicos y el código la genera.
Por ejemplo, en esta matriz de 6 filas por tres columnas (18 valores) donde hay 5 valores únicos (a, b, c, d y e)
Para que este código sea realmente útil debemos agregar algunas líneas para manejar errores que pueden ocurrir durante el proceso (por ejemplo, si el usuario selecciona un rango de datos con una sola celda o si aprieta el botón Cancel del InputBox). El código completo es el siguiente
Sub extraerUnicos_Hoja()
'extraer valores unicos de rangos de varias columnas o no continuos
'Jorge Dunkelman - JLD Excel Blog, mayo 2013
Dim collUnicos As New Collection
Dim vcollItem As Variant
Dim rngCell As Range, rngDatos As Range, rngLista As Range
Dim lCounter As Long
On Error GoTo errCancel 'si se aprieta Cancel
Set rngDatos = Application.InputBox(prompt:="Seleccione rango/s con datos", Type:=8)
If rngDatos.Count < 2 Then
MsgBox "Debe seleccionar un rango con mas de dos celda", vbCritical
Exit Sub
End If
Set rngLista = Application.InputBox(prompt:="Seleccione la primera celda de la lista", Type:=8)
If rngLista.Count <> 1 Then
MsgBox "Seleccione solamente una celda", vbCritical
Exit Sub
End If
On Error Resume Next
For Each rngCell In rngDatos
collUnicos.Add rngCell, Cstr(rngCell)
Next rngCell
On Error GoTo 0
lCounter = 0
For Each vcollItem In collUnicos
rngLista.Offset(lCounter, 0) = CStr(vcollItem)
lCounter = lCounter + 1
Next vcollItem
Exit Sub
errCancel:
Exit Sub
End Sub
Sugerencias y ayuda para Excel en español. Ejemplos de funciones y fórmulas, gráficos, automatización con Vba, Power Query y PowerPivot
domingo, mayo 19, 2013
sábado, mayo 11, 2013
Lista desplegable con combobox dinámico
En el pasado mostramos que una de las limitaciones de crear listas desplegables con Validación de Datos es la falta de la propiedad "autocompletar". En una nota del año 2008 mostramos como usar un cuadro combinado (combobox) de la colección de controles ActiveX para superar esta limitación.
Desde entonces he recibido muchas consultas sobre el tema. La más común es cómo hacer que el control aparezca en la celda activa y desaparezca después de haber elegido el valor.
Es decir, queremos mimetizar el comportamiento de las listas creadas con validación de datos, pero usando el cuadro combinado para disfrutar de la propiedad "autocompletar".
La técnica para hacerlo incluye, obviamente programar eventos (Vba). Mostraré aquí un ejemplo sencillo, que puede aplicarse a todo modelo. En nuestro ejemplo definimos un rango que contiene los nombres de los días de la semana, que será la fuente de los valores del combobox, y un rango en la hoja donde queremos utilizar el control.
Los pasos son los siguientes:
1 – Creamos un nombre definido que se refiere al rango que contiene los días de la semana
En este ejemplo usamos el cuadro de nombres para crear el nombre con facilidad. El rango está en la hoja "valores".
2 – En la hoja "lista" incrustamos un control cuadro combinado (combobox) de la colección de controles ActiveX
Ubicamos el control en el lugar deseado (en nuestro caso sobre la celda B2) asegurándonos que ocupe toda la celda.
En la propiedad ListFillRange ponemos el nombre definido que se refiere a la lista de valores (dia_semana).
De ser necesario cambiamos la definición de la fuente a un tamaño adecuado cambiando la definición de la propiedad Font.
Al finalizar el proceso desactivamos el botón "Modo Diseño" (lo controles no funcionan cuando Excel se encuentra en modo de diseño).
3 – Definimos el rango de la hoja "Lista"", donde queremos que aparezcan los controles cuando seleccionamos alguna celda del rango. En nuestro ejemplo el rango es B2:B20 ("rngDia")
4 – Programamos el evento Worksheet_SelectionChange en la hoja "lista" de manera que cuando el usuario seleccione alguna celda del rango "rngDia", el cuadro combinado aparezca sobre esa celda. El código, que va en el módulo de la hoja, es el siguiente
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Union(Target, Range("rngDia")).Address = Range("rngDia").Address Then
With ComboBox1
.Visible = True
.Top = ActiveCell.Top
.LinkedCell = ActiveCell.Address
End With
Else
ComboBox1.Visible = False
End If
End Sub
Lo que hace este código lo siguientes:
- Vuelve visible el control (cuando la celda activa no está en el rango deseado, el control es invisible, lo que hacemos definiendo la propiedad Visible como False)
- Definimos la propiedad Top del control de manera que coincida con el ángulo superior izquierdo de la celda activa
- Definimos que celda debe recibir el valor elegido (la celda activa).
La última línea del código oculta el control si la celda activa no pertenece al rango donde queremos que aparezca la lista desplegable.
El modelo funciona así
Podemos mejorar este código de manera que al alto y ancho del control se adapten dinámicamente el alto y ancho de la celda, definiendo las propiedades Height y Wide en el código del evento
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Union(Target, Range("rngDia")).Address = Range("rngDia").Address Then
With ComboBox1
.Visible = True
.Top = ActiveCell.Top
.Height = ActiveCell.Height
.Width = ActiveCell.Width
.LinkedCell = ActiveCell.Address
End With
Else
ComboBox1.Visible = False
End If
End Sub
De la misma manera podemos definer dinámicamente otras propiedades como el contenido de la lista (ListFillRange), el número de valores a mostrar en el cuadro (ListRows), etc.
Desde entonces he recibido muchas consultas sobre el tema. La más común es cómo hacer que el control aparezca en la celda activa y desaparezca después de haber elegido el valor.
Es decir, queremos mimetizar el comportamiento de las listas creadas con validación de datos, pero usando el cuadro combinado para disfrutar de la propiedad "autocompletar".
La técnica para hacerlo incluye, obviamente programar eventos (Vba). Mostraré aquí un ejemplo sencillo, que puede aplicarse a todo modelo. En nuestro ejemplo definimos un rango que contiene los nombres de los días de la semana, que será la fuente de los valores del combobox, y un rango en la hoja donde queremos utilizar el control.
Los pasos son los siguientes:
1 – Creamos un nombre definido que se refiere al rango que contiene los días de la semana
En este ejemplo usamos el cuadro de nombres para crear el nombre con facilidad. El rango está en la hoja "valores".
2 – En la hoja "lista" incrustamos un control cuadro combinado (combobox) de la colección de controles ActiveX
Ubicamos el control en el lugar deseado (en nuestro caso sobre la celda B2) asegurándonos que ocupe toda la celda.
En la propiedad ListFillRange ponemos el nombre definido que se refiere a la lista de valores (dia_semana).
De ser necesario cambiamos la definición de la fuente a un tamaño adecuado cambiando la definición de la propiedad Font.
Al finalizar el proceso desactivamos el botón "Modo Diseño" (lo controles no funcionan cuando Excel se encuentra en modo de diseño).
3 – Definimos el rango de la hoja "Lista"", donde queremos que aparezcan los controles cuando seleccionamos alguna celda del rango. En nuestro ejemplo el rango es B2:B20 ("rngDia")
4 – Programamos el evento Worksheet_SelectionChange en la hoja "lista" de manera que cuando el usuario seleccione alguna celda del rango "rngDia", el cuadro combinado aparezca sobre esa celda. El código, que va en el módulo de la hoja, es el siguiente
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Union(Target, Range("rngDia")).Address = Range("rngDia").Address Then
With ComboBox1
.Visible = True
.Top = ActiveCell.Top
.LinkedCell = ActiveCell.Address
End With
Else
ComboBox1.Visible = False
End If
End Sub
Lo que hace este código lo siguientes:
- Vuelve visible el control (cuando la celda activa no está en el rango deseado, el control es invisible, lo que hacemos definiendo la propiedad Visible como False)
- Definimos la propiedad Top del control de manera que coincida con el ángulo superior izquierdo de la celda activa
- Definimos que celda debe recibir el valor elegido (la celda activa).
La última línea del código oculta el control si la celda activa no pertenece al rango donde queremos que aparezca la lista desplegable.
El modelo funciona así
Podemos mejorar este código de manera que al alto y ancho del control se adapten dinámicamente el alto y ancho de la celda, definiendo las propiedades Height y Wide en el código del evento
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Union(Target, Range("rngDia")).Address = Range("rngDia").Address Then
With ComboBox1
.Visible = True
.Top = ActiveCell.Top
.Height = ActiveCell.Height
.Width = ActiveCell.Width
.LinkedCell = ActiveCell.Address
End With
Else
ComboBox1.Visible = False
End If
End Sub
De la misma manera podemos definer dinámicamente otras propiedades como el contenido de la lista (ListFillRange), el número de valores a mostrar en el cuadro (ListRows), etc.
Todo sobre listas desplegables en Excel, técnicas avanzadas y descarga gratuita de ejemplos en la Caja de Herramientas Excel - Listas Desplegables de JLD. Ver la nota o ir a la página de descarga de la guía.
viernes, abril 26, 2013
Intercambiar rangos en Excel
En todo proceso de diseño de una hoja en Excel es prácticamente inevitable tener que reordenar rangos (filas, columna o celdas).
Por lo general nos basta con arrastrar el rango a la posición deseada. Pero en no pocos casos debemos intercambiar posiciones, poner un rango en lugar del otro.
La técnica más común es usar Copiar (o Cortar) y Pegar, usando una ubicación transitoria, o arrastrando una de las columnas (o filas o celdas) a un área "libre" de la hoja y moviendo luego los objetos a la posición deseada
Una forma más eficiente es seleccionar uno de los rangos y manteniendo apretada la tecla Mayúsculas (Shift), apuntar con el mouse al borde indicado y arrastrar el rango a la posición del rango a intercambiar
Lo mismo puede hacer con celdas o filas.
En ciertas situaciones estas técnicas pueden ser engorrosas, en particular cuando queremos realizar el intercambio entre celdas que se encuentran en distintas columnas. Por ejemplo, si tenemos una lista de turnos de trabajo, donde los operarios piden con cierta frecuencia cambiar su turno:
Con un poco de código Vba (macros) podemos crear una herramienta útil para intercambiar celdas con facilidad:
Sub InterCambiarCeldas()
Dim rngCell2 As Range
Dim cellTemp As String
'verificar que se haya elegido solo una celda
If Selection.Count > 1 Then
MsgBox "Debe elegirse solo una celda", vbCritical
Exit Sub
End If
'verificar que la celda contenga un valor
If Len(ActiveCell) = 0 Then
MsgBox "Celda vacia", vbExclamation
Exit Sub
End If
On Error GoTo errCancel
Set rngCell2 = Application.InputBox(prompt:="Elija la celda a intercambiar", _
Title:="Celda a intercambiar", Type:=8)
If rngCell2.Count <> 1 Then
MsgBox "Debe elegirse solo una celda", vbCritical
Exit Sub
End If
If Len(rngCell2) = 0 Then
MsgBox "La celda elegida esta vacia", vbExclamation
Exit Sub
End If
cellTemp = ActiveCell.Value
ActiveCell.Value = rngCell2.Value
rngCell2 = cellTemp
Exit Sub
errCancel:
If Err.Number = 424 Then
MsgBox "Operacion cancelada", vbExclamation
Exit Sub
End If
End Sub
El código va en un módulo común del editor de Vba, y para que podamos usarlo en todo cuaderno abierto lo guardamos en un módulo del cuaderno Personal.xls(m). Par apoder usar la macro con facilidad le asignamos un atajo de teclado
o un ícono en la barra de acceso rápido
Con este código todo lo que tenemos que hacer es seleccionar la primera celda a intercambiar, apretar Ctrl-Mayúsculas-Enter para accionar la macro, elegir la segunda celda y apretar Ok. Si elegimos más de una celda o una celda vacía, aparece un aviso y el código termina; lo mismo si el usuario aprieta el botón Cancelar del al forma
Por lo general nos basta con arrastrar el rango a la posición deseada. Pero en no pocos casos debemos intercambiar posiciones, poner un rango en lugar del otro.
La técnica más común es usar Copiar (o Cortar) y Pegar, usando una ubicación transitoria, o arrastrando una de las columnas (o filas o celdas) a un área "libre" de la hoja y moviendo luego los objetos a la posición deseada
Una forma más eficiente es seleccionar uno de los rangos y manteniendo apretada la tecla Mayúsculas (Shift), apuntar con el mouse al borde indicado y arrastrar el rango a la posición del rango a intercambiar
Lo mismo puede hacer con celdas o filas.
En ciertas situaciones estas técnicas pueden ser engorrosas, en particular cuando queremos realizar el intercambio entre celdas que se encuentran en distintas columnas. Por ejemplo, si tenemos una lista de turnos de trabajo, donde los operarios piden con cierta frecuencia cambiar su turno:
Con un poco de código Vba (macros) podemos crear una herramienta útil para intercambiar celdas con facilidad:
Sub InterCambiarCeldas()
Dim rngCell2 As Range
Dim cellTemp As String
'verificar que se haya elegido solo una celda
If Selection.Count > 1 Then
MsgBox "Debe elegirse solo una celda", vbCritical
Exit Sub
End If
'verificar que la celda contenga un valor
If Len(ActiveCell) = 0 Then
MsgBox "Celda vacia", vbExclamation
Exit Sub
End If
On Error GoTo errCancel
Set rngCell2 = Application.InputBox(prompt:="Elija la celda a intercambiar", _
Title:="Celda a intercambiar", Type:=8)
If rngCell2.Count <> 1 Then
MsgBox "Debe elegirse solo una celda", vbCritical
Exit Sub
End If
If Len(rngCell2) = 0 Then
MsgBox "La celda elegida esta vacia", vbExclamation
Exit Sub
End If
cellTemp = ActiveCell.Value
ActiveCell.Value = rngCell2.Value
rngCell2 = cellTemp
Exit Sub
errCancel:
If Err.Number = 424 Then
MsgBox "Operacion cancelada", vbExclamation
Exit Sub
End If
End Sub
El código va en un módulo común del editor de Vba, y para que podamos usarlo en todo cuaderno abierto lo guardamos en un módulo del cuaderno Personal.xls(m). Par apoder usar la macro con facilidad le asignamos un atajo de teclado
o un ícono en la barra de acceso rápido
Con este código todo lo que tenemos que hacer es seleccionar la primera celda a intercambiar, apretar Ctrl-Mayúsculas-Enter para accionar la macro, elegir la segunda celda y apretar Ok. Si elegimos más de una celda o una celda vacía, aparece un aviso y el código termina; lo mismo si el usuario aprieta el botón Cancelar del al forma
Suscribirse a:
Entradas (Atom)