miércoles, 28 de enero de 2015

VBA: Evitar nombres de hojas duplicados en Excel.

Al hilo de una entrada anterior (ver) un lector preguntaba por la forma de ir generando hojas con nombres no duplicados, siguiendo una autonumeración:
...cómo hacer si tengo registros repetidos en la hoja origen que hace que el programa falle porque ya tiene una hoja con ese nombre,, pero necesito que si existe una hoja con ese nombre le ponga un _2 o un _3 y siga...


La idea es que a partir de un listado de una hoja de Excel ir generando las diferentes hojas, y en caso de repetición, añadirles un autonumérico:

VBA: Evitar nombres de hojas duplicados en Excel.



El trabajo consiste en generar una Function ('ExisteHoja') que determine si existe o no una hoja en nuestro libro de trabajo con el nombre nuevo a generar.
Esta Function la emplearemos en el procedimiento Sub 'CrearHojas', donde recorremos el rango de la hoja de cálculo con los nombres a dar, y con el que conseguimos ir renumerando aquellas hojas iguales...


Insertamos y ejecutamos el siguiente procedimiento 'CrearHojas':

Sub CrearHojas()
Dim HojaOrigen As Worksheet, HojaNueva As Worksheet
Set HojaOrigen = Sheets("origen")

'para recorrer los 11 registros del listado
For i = 1 To 11
    'duplicamos la Hoja 'modelo'
    Sheets("modelo").Copy after:=Worksheets(Worksheets.Count)
    Set HojaNueva = Sheets(Worksheets.Count)
    nombrehoja = Worksheets("origen").Cells(7 + i, 4).Value

    If ExisteHoja(CStr(nombrehoja)) = True Then
        x = x + 1
        If ExisteHoja(nombrehoja & "_" & x) = True Then
            y = y + 1
            'damos nombre a la hoja cuando existe duplicidad...
            HojaNueva.Name = nombrehoja & "_" & y
        Else
            HojaNueva.Name = nombrehoja & "_" & x
        End If
    Else
        'damos nombre a la hoja si no existiera
        HojaNueva.Name = nombrehoja
        x = 1: y = 1
    End If

Next i
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ExisteHoja(Nombre As String, Optional Libro As Workbook)
Dim sh As Worksheet
'controlamos el argumento Opcional
'que identifica el Libro donde Buscar...
If Libro Is Nothing Then Set Libro = ThisWorkbook

'controlamos la existencia de la hoja
'si tuvieramos un error es que existe...
On Error Resume Next
Set sh = Libro.Sheets(Nombre)
On Error GoTo 0

ExisteHoja = Not sh Is Nothing
End Function



El resultado sería:

VBA: Evitar nombres de hojas duplicados en Excel.

lunes, 26 de enero de 2015

Cursos Excel elearning con tutor: Mejora tus habilidades en Excel

Aumenta tu productividad... última oportunidad!
Aprende con los mejores y adquiere una buena base: Edición de Cursos de Excel y Macros online con tutor personal de Febrero de 2015.
Nunca estudiar fue tan fácil.


Los cursos de Excel y Macros abiertos para este mes de Febrero 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 Febrero de 2015; 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, 22 de enero de 2015

VBA: Copiar datos filtrados/visibles de una Tabla.

Veremos hoy un trabajo doble interesante:
Primero veremos cómo aplicar un filtro de fechas con macros sobre una Tabla (problemático habitualmente por una incompatibilidad de la configuración regional)
Segundo cómo copiar y pegar el resultado del filtro aplicado.


Partiremos de la siguiente hoja:

VBA: Copiar datos filtrados/visibles de una Tabla.



El trabajo del siguiente procedimiento consiste primero en aplicar un Filtro de fechas sobre el campo 'Fecha'; para ello deberemos tener la precaución de definir las variables con el tipo Long.
El motivo es salvar la configuración regional enfrentada de nuestra hoja de cálculo (dd/mm/aaaa) y la de VBA (mm/dd/yyyy)... al definirlo como Long (número entero), tratamos la fecha como lo que es para Excel, un número entero (en cualquier configuración regional, un número es un número...).


Una vez conseguido y aplicado el filtro, seleccionaremos sólo las celdas visible empleando el método SpecialCells(xlCellTypeVisible), a partir del cuerpo de la Tabla con la propiedad .DataBodyRange

Insertamos y ejecutamos el siguiente procedimiento, asignado al botón 'EXTRAER':

Sub RangoFecha()
'definimos las variables como Long para poder aplicar el autofiltro
'unica forma de salvar el problema de la configuración regional
Dim FechaDesde As Long, FechaHasta As Long
'definimos variables para aplicar el filtro sobre la tabla
FechaDesde = CDate(Range("B1").Value)
FechaHasta = CDate(Range("B2").Value)

'aplciamos el autofiltro sobre nuestro Tabla
ActiveSheet.ListObjects("TblDatos").Range.AutoFilter _
    Field:=1, Criteria1:=">=" & FechaDesde, Operator:=xlAnd, Criteria2:="<=" & FechaHasta

'aplciamos el copiado del resultado
Dim rng As Range

'trabajamos sólo sobre el cuerpo de la Tabla (.DataBodyRange)
'no sobre cabecera o fila totales
With ActiveSheet.ListObjects("TblDatos").DataBodyRange
    'definimos el rango visible
    'depurando el error por si no hubiera nada filtrado
    On Error Resume Next
    Set rng = .Resize(.Rows.Count, .Columns.Count).SpecialCells(xlCellTypeVisible)
    On Error GoTo 0
End With

'controlamos el fallo..
If rng Is Nothing Then
    'si no hubiera nada como resultado del filtro
    MsgBox "Sin datos a copiar"
Else
    'en caso hubiera algún dato
    MsgBox rng.Address                      'mostramos el rango de celdas..
    rng.Copy Destination:=Range("C23")      'copiamos el rango y lo pegamos a partir de C23
    Application.CutCopyMode = False         'liberamos el Portapapeles
End If

'finalizamos quitando el filtro aplicado
ActiveSheet.ListObjects("TblDatos").Range.AutoFilter Field:=1

End Sub

El resultado tras la ejecución de la macro es el siguiente... Tras aplicar el filtro vemos el Cuadro de mensaje que muestra el rango de celdas visibles dentro del cuerpo de la Tabla

VBA: Copiar datos filtrados/visibles de una Tabla.

Y se continuamos la ejecución veremos el copiado y pegado de estos datos:

VBA: Copiar datos filtrados/visibles de una Tabla.

martes, 20 de enero de 2015

VBA: Obtener el número de serie de un disco duro (o partición).

Quizá no le veas el sentido a la siguiente entrada, y puedas preguntarte para qué me puede servir conocer cuál es el número de serie de un disco duro (o partición).

Una posible utilidad es proteger tu libro de trabajo para que sólo pueda ser abierto desde tu equipo.. es decir, además de incorporar una contraseña al fichero, o añadir un procedimiento que pida usuario y contraseña, podríamos emplear este Número de Serie de tu disco duro como control de acceso.
Seguridad al cuadrado!!


Para ello necesitaremos la propiedad .SerialNumber que nos indica precisamente lo que neceistamos, nos devuelve el número de serie decimal que se utiliza para identificar de forma única un volumen de disco.

Para comprobar que lo que obtenemos con esta function es cierto, podemos acceder al botón de Inicio de Windows, y luego haremos clic en el cuadro de "Búsqueda", donde escribiremos 'cmd'.

VBA: Obtener el número de serie de un disco duro (o partición).



A continuación, en la ventana del Símbolo de sistema (o ventana de MS-DOS), escribe:
vol c:
o vol la unidad de la que quieras conocer el Número de Serie..

VBA: Obtener el número de serie de un disco duro (o partición).



En mi caso, el número de serie de la unidad C: es: 14AD E4A0

Ahoara, para comprobarlo, insertaremos el siguiente código de un procedimiento Function en un módulo general:

Function NumeroSerieUnidad(Optional ByVal LetraUnidad As String) As String
'Nota importante: Se recomienda activar la referencia de la librería 'Microsoft Scripting Runtime'
'Con esta función conseguimos el número de serie del disco duro (partición).
'Este Número de serie lo genera Windows al formatear la partición
Dim fso As Object, Drv As Object
Dim SerieUnidadDec As Long

Application.Volatile
'Creamos un objeto FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject")

'Asigamos la Letra de Unidad actual en caso de no especificarla en el argumento
If LetraUnidad <> "" Then
    Set Drv = fso.GetDrive(LetraUnidad)
Else
    Set Drv = fso.GetDrive(fso.GetDriveName(App.Path))
End If

With Drv
    'Devuelve True si la unidad especificada está preparada y False si no lo está.
    If .IsReady Then
        'SerialNumber Devuelve el número de serie decimal
        'que se utiliza para identificar de forma única un volumen de disco.
        SerieUnidadDec = Abs(.SerialNumber)
    Else
        SerieUnidadDec = -1
    End If
End With

'convertimos en Hexadecimal
SerieUnidadHex = Application.WorksheetFunction.Dec2Hex(SerieUnidadDec)

'Devovemos valor con la función
NumeroSerieUnidad = SerieUnidadHex
    
'Limpiamos las variables
Set Drv = Nothing
Set fso = Nothing
End Function



Si ejecutamos nuestra función en la hoja de cálculo, comprobamos que todo coincide...:

VBA: Obtener el número de serie de un disco duro (o partición).

miércoles, 14 de enero de 2015

Las funciones ELEGIR y BUSCARV: búsqueda matricial

Meses atrás expliqué una forma de realizar una búsqueda vertical matricial (ver).
La técnica consistía en concatenar los rangos de nuestro origen para conseguir una matriz 'virtual' de donde extraer la información buscada.
Hoy veremos una alternativa empleando la función ELEGIR de forma matricial.
Sobre el planteamiento del ejemplo siguiente:

Las funciones ELEGIR y BUSCARV: búsqueda matricial



La función matricial necesaria es, en la celda F1:
=BUSCARV(E1;ELEGIR({1\2};$A$1:$A$10&$B$1:$B$10;$C$1:$C$10);2;0)


La clave nuevamente es concatenar las columnas de datos $A$1:$A$10&$B$1:$B$10 para recomponer el código buscado... y sin duda el uso de ELEGIR con el que hemos reconstruido un origen de datos virtual de dos campos.
Podemos ver su representación en H1:I10:

Las funciones ELEGIR y BUSCARV: búsqueda matricial



Siendo sobre ese 'rango' sobre el que aplicamos la búsqueda con BUSCARV.
Notemos la potencia de esta función ELEGIR matricial, y es que podríamos reconstruir un rango matricial independientemente del orden del origen de datos real, por ejemplo:

lunes, 12 de enero de 2015

VBA: Añadir imágenes a los controles de Excel.

Hoy veremos algo sencillo pero muy vistoso, especialmente para dar ese toque personal y/o profesional a nuestros controles (sobre nuestro Formularios de usuario-UserForm).
Veremos la forma de añadir una imagen a nuestros controles (Botones-CommandButton, etc.)


Lo primero que aprenderemos será a buscar imágenes interesantes.. para ello podemos buscar directamente en Google > Imágenes... e introducir la temática deseada.

VBA: Añadir imágenes a los controles de Excel.


Por ejemplo 'relojes', y tras aplicar algunos criterios de búsqueda, sólo tendremos que buscar imágenes del tamaño adecuado (no muy grandes!!) y sobre todo asegúrate que tengan la extensión .gif, jpg o .bmp

VBA: Añadir imágenes a los controles de Excel.


Una vez encuentres aquel que te guste (y cumpla las condiciones), bastará que con el botón derecho del ratón sobre la imagen, presiones Guardar imagen como... e indiques el nombre y ruta donde guardarla...
Recuérdala porque en breve tendremos que recuperarla...


Por supuesto hay mil sitios donde encontrar imágenes. Por poner un par de ejemplos:
www.iconarchive.com
http://findicons.com
incluso a sitios web donde descargarte packs de iconos...


Lo que nos interesa, una vez tenemos la/las imágenes descargadas, accederemos a nuestro control (un CommandButton en nuestro ejemplo) dentro de un formulario, e iremos a la ventana de sus Propiedades:

VBA: Añadir imágenes a los controles de Excel.



En concreto nos fijaremos en dos de sus propiedades:
Picture
PicturePosition (con 12 posiciones de la imagen respecto del Texto del control)

Para cargar la imagen simplemente presionaremos los tres puntos de la propiedad Picture y en el explorador buscaremos la imagen que deseemos insertar...

VBA: Añadir imágenes a los controles de Excel.


Lo interesante es la posibilidad de esas 12 posiciones relativas de la imagen dentro del control...
Saber también que la imagen queda dentro del fichero y que no guarda ningún vínculo que la ruta de carga de la imagen.


Sin duda una manera sencilla de decorar nuestros controles y profesionalizar nuestros formularios.

jueves, 8 de enero de 2015

VBA: Tratando con errores - el objeto Err.

Una de los recursos más útiles en programación es aprender a 'lidiar' con los errores generados...
Normalmente podemos salvarlos (que no arreglarlos) con las instrucciones:
On Error Resume Next
On Erro GoTo ...
y algunos otros.

Sin embargo, disponemos de otra herramienta igualmente útil para atrapar y, sobre todo, tratar los errores controlados en nuestros procedimientos, el objeto Err.
Este objeto Err tiene un par de métodos asociados:
.Clear
.Raise
y algunas propiedades interesantes:
.Number
.Description
junto a
.Source
.Helpfile
.HelpContext
.LastDLLError


Con este objeto podemos controlar cualquier tipo de Error que se pueda generar en nuestra aplicación Excel.
Un listado (no completo) de los diferentes códigos de error sería:




Error Significado
3 Return sin GoSub
5 Argumento o llamada a procedimiento no válida
6 Desbordamiento
7 Memoria insuficiente
9 Subíndice fuera del intervalo
10 Esta matriz es fija o se encuentra temporalmente bloqueada
11 División por cero
13 No coinciden los tipos
14 Espacio para cadenas insuficiente
16 Expresión demasiado compleja
17 No se puede realizar la operación solicitada
18 Se ha producido una interrupción por parte del usuario
20 Resume sin Error
28 Espacio de pila insuficiente
35 No se ha definido Sub o Function
47 Hay demasiados clientes de la aplicación DLL
48 Error al cargar biblioteca DLL
49 La convención de llamada a la DLL no es correcta
51 Error interno
52 Nombre o número de archivo incorrecto
53 Archivo no encontrado
54 Modo de archivo incorrecto
55 El archivo ya está abierto
57 Error de E/S de dispositivo
58 El archivo ya existe
59 Longitud de registro incorrecta
61 Disco lleno
62 Se sobrepasó el final del archivo
63 Número de registro incorrecto
67 Demasiados archivos
68 Dispositivo no disponible
70 Permiso denegado
71 Disco no preparado
74 No se puede cambiar el nombre con una unidad de disco diferente
75 Error de acceso a la ruta o el archivo
76 No se ha encontrado la ruta de acceso
91 Variable de objeto o bloque With no establecido
92 Bucle For no inicializado
93 La cadena modelo no es válida
94 Uso no válido de Null
96 No se puede recibir eventos de objeto porque el objeto ya está desencadenando el número máximo de eventos que admite
97 No se puede llamar a una función friend de un objeto que no sea una instancia de una clase
98 Una llamada a una propiedad o un método no puede incluir una referencia a un objeto privado como un argumento o un valor de retorno
321 El formato de archivo no es válido
322 No se puede crear un archivo temporal necesario
325 El formato del archivo de recursos no es válido
380 El valor de la propiedad no es válido
381 El índice de la matriz de propiedades no es válido
382 No se admite Set durante la ejecución
383 No se admite Set (propiedad de sólo lectura)
385 Se necesita un índice de matriz de propiedades
387 No está permitido Set
393 No se admite Get durante la ejecución
394 No se admite Get (propiedad de sólo escritura)
422 No se encontró la propiedad
423 No se encontró la propiedad o el método
424 Se requiere un objeto
429 El componente ActiveX no puede crear el objeto
430 Esta clase no admite Automatización o no admite la interfaz esperada
432 No se encontró el nombre del archivo o de la clase durante la operación de Automatización
438 El objeto no admite esta propiedad o método
440 Error de Automatización
442 Se ha perdido la conexión con la biblioteca de tipos o con la biblioteca de objetos para procesos remotos. Haga clic en 'Aceptar' para eliminar la referencia.
443 El objeto de Automatización no tiene un valor predeterminado
445 El objeto no admite esta acción
446 El objeto no admite argumentos con nombre
447 El objeto no admite la configuración regional actual
448 No se encontró el argumento con nombre
449 El argumento no es opcional
450 El número de argumentos es incorrecto o la asignación de propiedad no es válida
451 El procedimiento Let de la propiedad no está definido y el procedimiento Get no ha devuelto un objeto
452 El número ordinal no es válido
453 No se encontró la función en la DLL especificada
454 No se encontró el recurso de código
455 Error en el bloqueo de los recursos de código
457 Esta tecla ya está asociada con un elemento de esta colección
458 La variable utiliza un tipo de Automatización no admitido en Visual Basic
459 El objeto o la clase no admite el conjunto de eventos
460 El formato del Portapapeles no es válido
461 No se encontró el método o el dato miembro
462 El equipo servidor remoto no existe o no está disponible
463 La clase no está registrada en el equipo local
481 Imagen no válida
482 Error de impresora
735 No se puede guardar el archivo en TEMP
744 No se encontró el texto de búsqueda
746 Sustituciones demasiado largas
1004 Error definido por la aplicación o el objeto. Este es un mensaje 'cajón de sastre' de error muy común. Este error se produce cuando un error no se corresponde con un error definido por VBA. En otras palabras, el error se define por Excel (o algún otro objeto) y se propaga de nuevo a VBA.


Igualmente se puede ver en este link una explicación de alguno de estos.
O bien generar una lista en una hoja de cálculo con este sencillo procedimiento...

Sub ListaErrores()
Range("A1").Select
On Error Resume Next
For i = 1 To 1050
    Error i
    ActiveCell.Value = Err.Number & " - " & Err.Description
    ActiveCell.Offset(1, 0).Select
Next i
End Sub


Debemos saber que los números o códigos de error en VBA para Excel (tanto los definidos por Visual Basic como los definidos por el usuario) se encuentran entre 0 y 65535, y que de 0 a 512 está reservado para errores del sistema; el intervalo 513 a 65535 está disponible para los errores definidos por los usuarios.
Ojo, porque al establecer en la propiedad .Number nuestro propio código de error en un Módulo de clase, agregaremos el código de error a la constante vbObjectError (recomendado); por ejemplo, para generar el número de error 1313, asignaríamos vbObjectError +1313 a la propiedad .Number.


Y qué ocurre si quiere personalizar un error en mi procedimiento??. En ese caso utilizaremos los métodos que nos proporciona Err, en concreto .Raise.

Veamos el siguiente ejemplo:

Sub TestErrorPersonal()
Dim Entero As Integer
'controlamos un posible error en nuestro código
On Error GoTo TestErrorPersonal_Error
Entero = InputBox("Introduce un número entero", "Comprobación errores", 13)

If Entero > 10 Then
    'cuando se cumpla la condición
    'creamos un Error personalizado y su descripción
    Err.Raise Number:=1313, Description:="El valor " & Entero & " supera el límite establecido (10)..."
Else
  MsgBox "El entero dado es " & Entero
End If

Exit Sub
TestErrorPersonal_Error:
'Muestra un mensaje con nuestro Número de Error y su descripción
MsgBox "Error generado # " & Err.Number & " - " & Err.Description
'Borra todas las propiedades establecidas del objeto Err
Err.Clear
Exit Sub
End Sub



Como se aprecia rápidamente hemos creado nuestro propio Error (el número 1313), al que hemos añadido nuestra propia descripción.
En el procedimiento se pide al usuario introduzca un valor... se verifica si éste supera el valor establecido (10), en cuyo caso creamos un error personalizado.
Al estar controlado el error, con la instrucción (On Error GoTo TestErrorPersonal_Error), se ejecuta la parte final de macro, mostrando un mensaje construido empleando las propiedades .Err.Number y Err.Description.
Acabamos liberando nuestro Error.

VBA: Tratando con errores - el objeto Err.



En general, que debemos conocer de estos métodos del objeto Err:
método .Raise (.Raise number, source, description, helpfile, helpcontext): todos los argumentos son opcionales, excepto .Number. Sin embargo, si utilizamos .Raise sin especificar algunos de los argumentos, y los valores de las propiedades del objeto Err no se han borrado (con .Clear), dichos valores se conservarán para el error actual.

método .Clear (objeto.Clear): empleamos .Clear para borrar explícitamente el objeto Err después de controlar un error, por ejemplo, cuando hemos establecido el tratamiento diferido de un error con On Error Resume Next.


Algunas de las propiedades de Err:
propiedad .Number: Al devolver un error definido por el usuario desde un objeto, es recomendable establecer el Err.Number agregando el número que ha seleccionado como código de error a la constante vbObjectError.

propiedad .Description: La propiedad .Description contiene una breve descripción del error. necesaria para notificar al usuario el error generado. OJO, porque si no se rellena Description y el valor de Number corresponde a un error en tiempo de ejecución de Visual Basic, la cadena que devuelve la función Error se coloca en Description cuando se genera el error.

propiedad .Source: Con la propiedad .Source especificamos una expresión de cadena que representa al objeto que generó el error; la expresión es normalmente el nombre de clase o el identificador programático del objeto. Podemos utilizar .Source para proporcionar información al usuario cuando el código no pueda tratar un error generado en un objeto al que se ha tenido acceso.
Por ejemplo, si se tiene acceso a Microsoft Excel y éste genera un error División por cero, Microsoft Excel establecerá en Err.Number su código de error correspondiente y Excel.Application en Source.

miércoles, 7 de enero de 2015

Mezclando datos en Excel.

Un compañero me planteó la manera de conseguir mezclar los datos de dos grupos diferentes (frutas y colores):

como puedo con macro o función combinar cada valor de la primera columna con todos los valores de la segunda columna, es decir combinar el primer valor de la primera columna con todos los de la segunda columna, luego pasar al segundo valor de la primera columna y combinarlo con todos los de la segunda y asi sucesivamente hasta tener en una nueva columna todos los valores resultantes de combinar cada uno de los valores de la primera columna con TODOS  y cada uno de los valores de la segunda columna, con un espacio por medio claro...


Estos son nuestros datos de partida:

Mezclando datos en Excel.



El objetivo es sencillo, para cada elemento del primer grupo combinarlo con el del segundo...
Como en el primero tenemos 8 elementos y el segundo 5, deberemos llegar a 40 (8 x 5) nuevos elementos:

Mezclando datos en Excel.



Comenzamos entonces el ejercicio.
En primer lugar en la columna D (en la celda D1) insertamos la siguiente fórmula, que arrastramos 40 filas abajo:
=(MULTIPLO.INFERIOR.MAT(FILA()-1;CONTARA(Tabla1[dato2]))/CONTARA(Tabla1[dato2]))+1

lo que conseguimos con esta fórmula es arrastrar tramos/intervalos tomados de 5 en 5 (determinado por CONTARA(Tabla1[dato2]) ), que es el número de elementos del segundo grupo (colores), llegando hasta el valor 8.
Adicionalmente, en la columna E, incluimos la fórmula:
=INDIRECTO(DIRECCION(D1+1;1))
con la que retornamos el valor de de la celda correspondiente de la columna A, esto es, los elementos del primer grupo (frutas).


En la segunda parte del análisis, en la columna F, trabajamos sobre el segundo grupo de colores.
Insertamos la fórmula:
=CONTAR.SI($D$1:D1;D1)
con el rango corrido desde $D$1, lo que nos asegura un recorrido de 1 a 5 cíclico para cada tramo calculado anteriormente (de 5 en 5) de 1 a 8.
De igual manera en la columna F insertamos:
=INDIRECTO(DIRECCION(F1+1;2))
obteniendo el valor de la celda correspondiente de la columna B, es decir, de los valores de 'colores'.


En la columna H recogemos nuestros frutos (jeje...), insertamos un concatenado de las columnas E y G:
=E1& " " &G1
llegando a nuestro objetivo.


Por supuesto, todo esto se podría conseguir con una sencilla macro...

Sub MezclandoDatos()
'iniciamos contador
x = 1
'recorremos cada elemento del primer grupo (frutas)
For Each celda1 In Range("Tabla1[dato1]")
    'recorremos cada elemento del segundo grupo (colores)
    For Each celda2 In Range("Tabla1[dato2]")
        'controlamos que no haya elementos vación mezclados con otros existentes
        If celda1.Value = "" Or celda2.Value = "" Then
            'nothing
        Else
            'llevamos la mezcla a la hoja..
            Sheets("Hoja1").Cells(x, 1).Value = celda1.Value & " " & celda2.Value
            x = x + 1
        End If
    Next celda2
Next celda1

End Sub