sábado, 20 de marzo de 2010

C# - Buscar en una lista contenido repetido

 

Introducción

Sucede muchas veces que tenemos una lista con diversos ítems y es necesario saber cuales se repiten en base a un criterio especifico.

Si s esta desarrollando una aplicación con .net 3.5 o superior, lo primero que viene a la mente es utilizar linq, pero que sucede si se desarrolla sin esta tecnología, como resolver el problema.

Es por ello que este articulo propondrá un caso practico en donde se intentara brindar una solución.

El problema

Se dispone de una lista de archivos en una variable del tipo List<string>, y de esta se quiere saber cuantos ítems con distinta extensión se encuentran cargados.

Usando Linq

Esta primera solución seria la clásica, si uno dispone de Linq, como se observara es muy simple.

[C#]

static void Main(string[] args)
{

    List<string> lista = new List<string>()
    {
        "produtos.txt", "reporte.doc", 
        "listado.txt", "ventas.xls", 
        "servicios.doc", "detalles.txt"
    };

    var query = from item in lista
                let extension = item.Split('.')[1]
                group item by extension into g
                select new { Key = g.Key, Values = g };

    foreach (var item in query)
    {
        Console.WriteLine(string.Format("{0} - {1}", item.Key, item.Values.Count()));
    }

    Console.ReadLine();

}

[VB.NET]

Friend Shared Sub Main(ByVal args As String())

    Dim lista As New List(Of String)()
    lista.Add("produtos.txt")
    lista.Add("reporte.doc")
    lista.Add("listado.txt")
    lista.Add("ventas.xls")
    lista.Add("servicios.doc")
    lista.Add("detalles.txt")


    Dim query = From item In lista _
                 Let extension = item.Split("."c)(1) _
                 Group item By Key = extension Into Group _
                 Select Key, Values = Group

    For Each item In query
        Console.WriteLine(String.Format("{0} - {1}", item.Key, item.Values.Count()))
    Next

    Console.ReadLine()

End Sub

 

Usando Dictionary<>

La alternativa sin Linq, involucra el uso de diccionarios genéricos, al cual mediante un proceso secuencial será cargado con la información agrupada.

[C#]

static void Main(string[] args)
{
    List<string> lista = new List<string>()
    {
        "produtos.txt", "reporte.doc", 
        "listado.txt", "ventas.xls", 
        "servicios.doc", "detalles.txt"
    };

    Dictionary<string, int> contador = new Dictionary<string, int>();

    foreach(string item in lista)
    {
        string extension = item.Split('.')[1];

        if (contador.ContainsKey(extension))
            contador[extension]++;
     else
            contador.Add(extension, 1);
      
    }

    //
    // se muestra el resultado en pantalla
    //
    foreach (KeyValuePair<string, int> item in contador)
    {
     Console.WriteLine(string.Format("{0} - {1}", item.Key, item.Value));
    }

    Console.ReadLine();
}

[VB.NET]

Friend Shared Sub Main(ByVal args As String())

    Dim lista As New List(Of String)()
    lista.Add("produtos.txt")
    lista.Add("reporte.doc")
    lista.Add("listado.txt")
    lista.Add("ventas.xls")
    lista.Add("servicios.doc")
    lista.Add("detalles.txt")

    Dim contador As New Dictionary(Of String, Integer)()

    For Each item As String In lista
        Dim extension As String = item.Split("."c)(1)

        If contador.ContainsKey(extension) Then
            contador(extension) += 1
        Else
            contador.Add(extension, 1)
        End If
    Next

    '
    ' se muestra el resultado en pantalla
    '
    For Each item As KeyValuePair(Of String, Integer) In contador
        Console.WriteLine(String.Format("{0} - {1}", item.Key, item.Value))
    Next

    Console.ReadLine()
End Sub

En el ejemplo se se recorre la lista preguntando si la extensión ya ha sido registrada, para esto se hace uso del método ContainsKey() que proporciona el diccionario.

Si el ítem se encuentra se incremente en uno, sino se agrega por primera vez a la colección.

Al final se recorre los acumulado y se despliega en pantalla.

6 comentarios:

  1. Genial, contesta a una pregunta que dejé en los foros de msdn. Gracias!

    ResponderEliminar
  2. hola Leandro, primero que nada quiero decirte que sos muy bueno ya que muchas de las dudas que tengo desde que comence con .net las evacuo con tus explicaciones. queria preguntarte si existe la posibilidad de buscar un objeto en una lista de objetos. por ejemplo:
    list gente = new list<personas<();
    gente.add("juan",23,"masculino");
    gente.add("pepe",53,"masculino");
    gente.add("ana",33,"femenino");
    mas abajo en el codigo quiero agregar otra persona y es :
    persona nueva = new persona();
    persona.nombre = "ana";
    persona.edad = "33";
    persona.sexo = "femenino";
    y ahora es donde quiero hacer lo siguiente :
    if(gente.contains(persona) == false)
    { gente.add(persona)}

    esta bien esto??
    Desde ya gracias por tu ayuda.

    ResponderEliminar
    Respuestas
    1. hola
      Podrias buscarlo usando linq

      bool existe = gente.Any(x=> x.nombre == "ana");
      if(existe){ ...

      Ahora bien si lo que quieres es compara una entidad completa vas a tener que implementar alguna interfaz como ser IEquatable<>

      Change how List.Contains() works

      entonces si vas a poder usar el Contains() ya que la instance define el metodo donde indicas como se compara la entidad con otra
      saludos

      Eliminar
  3. Hola Leandro, necesito el codigo para poder recorrer dos listas con valores y en una tercera lista mostrar la suma de cada valor de cada lista.
    osea: lista 1 y lista 2 contiene tarea,duracion y en la otra lista mostar tarea y resultado.
    soy novato y quiero aprender gracias

    ResponderEliminar
  4. o tambien si cargo cada lista a un datagridview en wpf y recorrer cada datagrid y colocar la suma de esas celdas en una celda. gracias

    ResponderEliminar
  5. como funcionaria esto con los numeros, digamos que yo quiero saber cual es el numero que mas se repite
    Por cierto muy buen aporte

    ResponderEliminar