martes, 20 de mayo de 2014

Receta No. 2-18 en C#: Remoción de Elementos Duplicados en un Arreglo o en una Colección

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
4. Práctica: Código Fuente C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

Ya en la receta previa aprendimos cómo usar LINQ para seleccionar/buscar los elementos de un arreglo o de una colección usando consultas que describen a través de una condición o criterio los elementos de datos que esperamos obtener de este tipo de estructuras de datos. Ahora, llega la oportunidad de realizar otra operación importante en la consulta o búsqueda de elementos: distinción y remoción de elementos de datos duplicados a través del método Distinct.

1. Problema

Contamos con una ingente cantidad de datos proveniente de archivos de texto. Después de un detenido análisis, el gestor de documentación de los sistemas legados nos ha informado acerca de haber encontrado redundancia de información de esa fuente de datos. Éste miembro del equipo de gestión de la información nos ha solicitado implementar un sistema batch que permita remover los duplicados existentes en los registros y generar un contenedor libre de redundancia.

2. Solución

El equipo de programadores encargados de lidiar con colecciones y arreglos, conocen muy bien que este tipo de estructuras de datos poseen el método Distinct (sobrecargado) para obtener un listado. Con este método vamos a poder resolver el problema de redundancia de datos en la fuente de datos y generar una nueva fuente de datos libre y protegida contra este fenómeno de duplicidad de datos. 

3. Discusión de la Solución

En esta sección entraremos en los detalles del método Distinct (el cual forma parte de las características nativas de LINQ).

3.1 Método Distinct

El método Distinct [2] se haya definido en la clase estática Enumerable [3]. Este método posee la siguiente lista sobrecargada:
Lista sobrecargada del método Distinct
Figura 1. Lista sobrecargada del método Distinct [2].

3.2 Versión Distinct(IEnumerable)

Esta versión del método Distinct [4] retorna una colección o arreglo de elementos no-duplicados; para ello utiliza el comparador predeterminado de igualdad sobre los elementos de datos. Veamos su firma:

public static IEnumerable Distinct(this IEnumerable source)

Opera sobre cualquier tipo de datos gracias a la genericidad del metodo -TSource-; en cuanto al parámetro, corresponde con una instancia de la colección sobre la que se actuará para remover los elementos de datos duplicados: source (this). El tipo de retorno, corresponde con una instancia de IEnumerable genérica: colección con elementos no-duplicados (distintos).

Vale apuntar [4] que la secuencia o el resultado devuelto por este método no posee ningún orden en particular, solamente los elementos no duplicados, debido a que el comparador predeterminado opera sobre la naturaleza de los datos integrados.

Desde [4]:
This method is implemented by using deferred execution. The immediate return value is an object that stores all the informacion that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
Ahora veamos un ejemplos en donde comprendamos mejor su utilidad:

Remoción de números enteros duplicados:

List<int> edades = new List<int> { 21, 46, 46, 55, 17, 21, 55, 55};

IEnumerable<int> edadesDistintas = edades.Distinct();

Console.WriteLine("\nEdades diferentes:");
foreach (int edad in edadesDistintas)
{
Console.WriteLine("\t{0}", edad.ToString());
}


Resultado:

Edades diferentes:
 21
 46
 55
 17

El uso de Distinct en estas situaciones es muy sencillo, basta con invocarlo sobre un objeto de una colección y crear una variable de tipo IEnumerable, como en:

IEnumerable<int> edadesDistintas = edades.Distinct();

A este método le podemos cambiar el comportamiento de comparación. Esto nos resulta útil para los tipos que creemos a partir de nuestro modelo del mundo del problema. Para lograrlo necesitamos que nuestros tipos (e.g., clases, interfaces) implementen la interfaz IEquatable [5]. Con este ejemplo podremos ver cómo lograrlo:

Archivo Producto.cs:
En la clase Producto (líneas 20-57) implementamos la interfaz IEquatable para implementar el método Equals [6] (líneas 34-47) para comparaciones personalizadas.

> Prueba de ejecución.

Resultado:

Teclado - 31001
Kindle - 97001
Mouse - 72004

3.3 Versión Distinct(IEnumerable, IEqualityComparer)

Con esta versión de Distinct [7], a diferencia de la anterior, nos permite especificar una instancia de comparación de igualdad -IEqualityComparer- [8] para la generación de una secuencia sin duplicados. Estos son los miembros a implementar de la interfaz en mención:
Métodos abstractos de la interfaz IEqualityComparer
Figura 2. Métodos abstractos de la interfaz IEqualityComparer [8].
A diferencia de la interfaz IEquatable, la interfaz IEqualityComparer (veremos su uso en la sección práctica) requiere que se cumpla un contrato más estricto (respecto a la cantidad de métodos abstractos): se debe implementar los métodos abstractos:
  • Equals(T,T)
  • GetHashCode(T)

4. Práctica: Código Fuente C#

Vamos a reutilizar la clase Fruta de la receta previa, pero le vamos a realizar algunas modificaciones: debe implementar la interfaz IEqualityComparer.

Archivo Fruta.cs:

Compilación:


  1. csc /target:exe Fruta.cs

> Prueba de ejecución.

Resultado:

Lista de frutas sin duplicados:
Fruta: manzana - verde
Fruta: pera - verde
Fruta: mora - rojo
Fruta: tomate - rojo
Fruta: cereza - rojo
Fruta: mango - amarillo
Fruta: banano - amarillo
Fruta: naranja - naranaja
Fruta: melón - amarillo

5. Conclusiones

Hemos de ser cuidadosos cuando manipulemos información en estructuras de datos, me refiero, con el almacenamiento de elementos de datos duplicados, dado que nos puede conducir a errores en la edición de la información y obtener datos inconsistentes. Para esto, el Framework .NET soporta la remoción de duplicados en arreglos y colecciones a través del método Distinct.

6. Glosario

  • Batch
  • Comparador
  • Consulta
  • Duplicidad de la información
  • Gestor documental
  • LINQ
  • Redundancia

7. Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: Enumerable.Distinct Method (System.Linq) - http://msdn.microsoft.com/en-us/library/vstudio/system.linq.enumerable.distinct(v=vs.90).aspx
[3]: Enumerable Class (System.Linq) - http://msdn.microsoft.com/en-us/library/vstudio/system.linq.enumerable(v=vs.90).aspx
[4]: Enumerable.Distinct(TSource) Method (IEnumerable(TSource)) (System.Linq) - http://msdn.microsoft.com/en-us/library/vstudio/bb348436(v=vs.90).aspx
[5]: IEquatable(T) Interface (System) - http://msdn.microsoft.com/en-us/library/ms131187.aspx
[6]: IEquatable(T).Equals Method (System) - http://msdn.microsoft.com/en-us/library/ms131190.aspx
[7]: Enumerable.Distinct(TSource) Method (IEnumerable(TSource), IEqualityComparer(TSource)) (System.Linq) - http://msdn.microsoft.com/en-us/library/vstudio/bb338049(v=vs.90).aspx
[8]: IEqualityComparer(T) Interface (System.Collections.Generic) - http://msdn.microsoft.com/en-us/library/vstudio/ms132151(v=vs.90).aspx


J

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.