Blog alskare

.Net y lo que surja

C# Personalizar la ordenación de listas con IComparable

Posted by alskare en 19/09/2009

Hace poco, designios extraños de la vida, me ha tocado hacer una ordenación de una serie de objetos string tal como se hacía en la antigüedad. Cuando hablo de la antigüedad, tan sólo dejo correr un poco la memoria hasta los tiempos en que vestía con un jersey de cuello alto y unos pantalones cortos. Entonces, al pasar por los pasillos de las aulas, no era nada extraño escuchar una especie de canto gregoriano (pero a lo pobre) en el que una panda de alumnos que pensaban más en el pájaro del árbol de enfrente que en lo que estaban haciendo, solían recitar: a, b, c, ch (che), d, e , f, g, h, i, j, k, l, m, n, ñ, o, p, q, r, rr (erre doble), s, t, u, v, w, x, y, y zeta.

Bueno, el caso es que esta vez no me ha tocado hacer nada sobre la rr, pero sí que he tenido que hacer un sistema de ordenación en el que quedase contemplado la letra che. Por cierto, dicen que la curiosidad mata al gato y, como soy muy cotilla, se me ha ocurrido buscar la letra en la R.A.E y, ¡sorpresa!, ¡Todavía existe! (http://buscon.rae.es/draeI/SrvltConsulta?TIPO_BUS=3&LEMA=che).

Siempre me voy por las ramas, así que… al lío:

Vamos a crear un ejemplo con la socorrida clase Persona y, como haremos uso de una colección List<Persona>, le implementamos la interfaz IComparable. Aunque ya sabemos que el resultado no será el que andamos buscando, nos dará una idea de cómo nos realizaría la ordenación cualquier colección que nos permita realizar una ordenación de los objetos Persona.

using System;
using System.Collections.Generic;

namespace OrdenacionStrings
{
    class Ordenando
    {
        static void Main(string[] args)
        {
            List<Persona> gente = new List<Persona>();
            gente.Add(new Persona("Jaime", "Cebado Sánchez"));
            gente.Add(new Persona("Victoria", "Chacón Gómez"));
            gente.Add(new Persona("Anibal", "Chacón Gómez"));
            gente.Add(new Persona("Andrés", "Cullera Ramón"));
            gente.Add(new Persona("Juan Manuel", "Damián Rodriguez"));

            gente.Sort();

            foreach (Persona p in gente)
            {
                Console.WriteLine(p.Apellidos + ", " + p.Nombre);
            }
            Console.ReadKey();
        }
    }


    public class Persona : IComparable
    {
        public string Nombre { get; set; }
        public string Apellidos { get; set; }

        public Persona(string nombre, string apellidos)
        {
            Nombre = nombre;
            Apellidos = apellidos;
        }

        public int CompareTo(object obj)
        {
            try
            {
                Persona p = (Persona)obj;
                return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
            }
            catch { return 0; }
        }
    }

}

En el momento en que ejecutemos el código, veremos que la ordenación que hace es la actual, es decir, tendremos un resultado en el cual, los “Chacón” estarán antes de “Cullera”, cuando en realidad deberían salir después, puesto que contienen la letra (o dígrafo) che.

Cebado Sánchez, Jaime
Chacón Gómez, Anibal
Chacón Gómez, Victoria
Cullera Ramón, Andrés
Damián Rodriguez, Juan Manuel

Una de las ventajas de la Interfaz IComparable es que podemos implementar el método CompareTo como nos plazca (gracias, OO) y, con una serie de pequeños cambios en la clase Persona, podemos realizar un control de tal manera que, analizando las dos primeras letras de la cadena, podemos indicarle si la ch irá antes o después de la c.

Así, con una implementación distinta de la anterior del métodos CompareTo, el resultado será el deseado desde el principio:

public int CompareTo(object obj)
 {
     try
     {
         Persona p = (Persona)obj;
         if (!this.Apellidos.ToLower().StartsWith("ch"))
         {
             return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
         }
         else
         {
             if (!p.Apellidos.ToLower().StartsWith("ch"))
             {
                 return (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") * -1);

                 /* la línea anterior intenta simplificar el conjunto de operaciones siguientes.
                  * Sólo se trata de realizar una inversión en la comparación de la ch y la d
                  */ 

                 //if (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") == 1)
                 //    return -1;
                 //else if (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") == -1)
                 //    return 1;
                 //else return 0;
             }
             else
             {
                 return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
             }
         }
     }
     catch { return 0; }
 }
    }

Resultado obtenido:

Cebado Sánchez, Jaime
Cullera Ramón, Andrés
Chacón Gómez, Anibal
Chacón Gómez, Victoria
Damián Rodriguez, Juan Manuel

 

Dejo aquí el código de la clase al completo para no tener que ir buscando entre los “cachitos” de código del post.

using System;
using System.Collections.Generic;

namespace OrdenacionCorrecta
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Persona> gente = new List<Persona>();
            gente.Add(new Persona("Jaime", "Cebado Sánchez"));
            gente.Add(new Persona("Victoria", "Chacón Gómez"));
            gente.Add(new Persona("Anibal", "Chacón Gómez"));
            gente.Add(new Persona("Andrés", "Cullera Ramón"));
            gente.Add(new Persona("Juan Manuel", "Damián Rodriguez"));

            gente.Sort();

            foreach (Persona p in gente)
            {
                Console.WriteLine(p.Apellidos + ", " + p.Nombre);
            }
            Console.ReadKey();
        }
    }


    public class Persona : IComparable
    {
        public string Nombre { get; set; }
        public string Apellidos { get; set; }

        public Persona(string nombre, string apellidos)
        {
            Nombre = nombre;
            Apellidos = apellidos;
        }


        public int CompareTo(object obj)
        {
            try
            {
                Persona p = (Persona)obj;
                if (!this.Apellidos.ToLower().StartsWith("ch"))
                {
                    return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
                }
                else
                {
                    if (!p.Apellidos.ToLower().StartsWith("ch"))
                    {
                        return (string.Compare((p.Apellidos + " " + p.Nombre).ToLower(), "d") * -1);
                    }
                    else
                    {
                        return (this.Apellidos + " " + this.Nombre).CompareTo(p.Apellidos + " " + p.Nombre);
                    }
                }
            }
            catch { return 0; }
        }
    }
    
}

2 comentarios to “C# Personalizar la ordenación de listas con IComparable”

  1. alskare said

    No entiendo. De hecho, creo que he aprovechado, precisamente, la implementación de la interfaz para poder personalizar un tipo de ordenación. Si hubiese hecho uso de alguna clase que implementase la interfaz IComparable, me hubiese encontrado que no me ordenaría las letras CH, RR y LL como yo quería. De esta manera, puedo clasificar las distintas instancias de la clase tal como pretendía (en el ejemplo, por sencillez, sólo incluí la CH).

    Vamos, no sé si existe alguna otra manera de hacerlo, pero esto es lo que vi más cómodo.

  2. Netsky said

    buena explicacion, aunque hubiera sido mejor implementar la interfaz generica no lo crees???

    Felicidades por tu blog…

Sorry, the comment form is closed at this time.

 
A %d blogueros les gusta esto: