Sortowanie listy po dowolnej z właściwości

0

Cześć.

Pisze program w którym to mam pewną listę danych osobowych. Lista danych zawiera w sobie zbiór pewnych informacji w stylu "Imię", "nazwisko", "nr telefonów" itp ogółem danych jest chyba z 15.

Założenie jest takie, że użytkownik może sortować sobie listę po DOWOLNEJ danej z listy. Czyi user może sobie posortować listę według imienia, ale jeśli ma ochotę to także po dacie urodzenia.

Napisałem taką krótką funkcję:
public void SortBrithday()
{
this.datas.Sort((x, y) => ((Contact)x).Brithday.CompareTo(((Contact)y).Brithday));
}

ps: x i y konwertuję na odpowiedni typ z powodu dziedziczenia, które występuje w aplikacji - w tym problemie to bez znaczenia.

Funkcja działa, ale jak wiadomo sortuje tylko po urodzinach.
Chciałbym jednak napisać funkcję ogólną na zasadzie:

void Sort(nazwaAtrybutu)
{
  this.datas.Sort((x, y) => ((Contact)x).nazwaAtrybutu.CompareTo(((Contact)y).nazwaAtrybutu));
}

Wie ktoś może jak takie coś zrobić?

(Rev: pozwoliłem sobie lekko zmienić nazwę tematu)

0

Mógłbyś może nieco bardziej to opisać? Bo ja tam nie widzę różnych atrybutów klasy, tylko dziedziczenie po prostym typie string. I tylko po jednej "kolumnie".
Ja mam zaś wiele kolumn.

0

Sortowanie *, a nie dziedziczenie :). Pisałem o jednym myślałem o drugim.

0

O dziękuję bardzo. Z tego da się coś wyrzeźbić.

1

Specem nie jestem, ale na chłopski rozum...

private static IEnumerable<Osoba> _baza;

static void Main( string[] args )
{
    _baza = new Osoba[]
    {
        new Osoba("Jan", "Nowak", new DateTime(1972, 01, 03)),
        new Osoba("Mirosław", "Kowalski", new DateTime(1986, 03, 21)),
        new Osoba("Waldemar", "Wiśniewski", new DateTime(1982, 05, 13))
    };  

    WyswietlBaze(_baza.OrderBy(osoba => osoba.Imie));
    WyswietlBaze(_baza.OrderBy(osoba => osoba.Nazwisko));
    WyswietlBaze(_baza.OrderBy(osoba => osoba.DataUrodzenia));

    WyswietlBaze(Posortuj("Imie"));
    WyswietlBaze(Posortuj("Nazwisko"));
    WyswietlBaze(Posortuj("DataUrodzenia"));

    Console.ReadLine();
}

public static IEnumerable<Osoba> Posortuj( string nazwaAtrybutu )
{
    return _baza.OrderBy(osoba => typeof(Osoba).GetField(nazwaAtrybutu).GetValue(osoba));
}

public static void WyswietlBaze(IEnumerable<Osoba> baza )
{
    Console.WriteLine("{0,-10}\t{1,-10}\t{2,-10}", "Imie", "Nazwisko", "Urodzony");
    foreach(var osoba in baza)
        Console.WriteLine(osoba);
    Console.WriteLine();
}

public class Osoba
{
    public string Imie;
    public string Nazwisko;
    public DateTime DataUrodzenia;

    public Osoba( string imie, string nazwisko, DateTime dataUrodzenia )
    {
        this.Imie = imie;
        this.Nazwisko = nazwisko;
        this.DataUrodzenia = dataUrodzenia;
    }

    public override string ToString()
    {
        return String.Format("{0,10}\t{1,10}\t{2,10}", this.Imie, this.Nazwisko, this.DataUrodzenia.ToString("dd.mm.yyyy"));
    }
}

Zaraz sobie przejrzę podany wyżej link.

1

Faktycznie można wykorzystać expression trees do budowy delegaty-selektora, który przekażemy do OrderBy. Dodatkowo warto by było je scache'ować - kompilacja jest dość kosztowna. Możemy całość zrobić jako metodę rozszerzającą IEnumerable<T>. Dla wielu wywołań z tymi samymi parametrami rozwiązanie to powinno być szybsze od powyższego z refleksją.

public static class Extensions
{
    static Dictionary<KeyValuePair<Type, string>, Delegate> selectors = new Dictionary<KeyValuePair<Type, string>, Delegate>();

    static Func<T, object> GetSelector<T>(string propertyOrField)
    {
        var selectorKey = new KeyValuePair<Type, string>(typeof(T), propertyOrField);

        Func<T, object> selector;

        if (selectors.ContainsKey(selectorKey))
            selector = (Func<T, object>)selectors[selectorKey];
        else
        {
            var parameter = Expression.Parameter(typeof(T));

            var selectorExpression =
                Expression.Lambda<Func<T, object>>(
                    Expression.Convert(
                        Expression.PropertyOrField(parameter, propertyOrField),
                        typeof(object)),
                    parameter);

            selector = selectorExpression.Compile();

            selectors[selectorKey] = selector;
        }

        return selector;
    }

    public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, string propertyOrField)
    {
        return enumerable.OrderBy(GetSelector<T>(propertyOrField));
    }

    public static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> enumerable, string propertyOrField)
    {
        return enumerable.OrderByDescending(GetSelector<T>(propertyOrField));
    }
}

Tę klasę wystarczy umieścić gdzieś w projekcie i upewnić się by była widoczna, gdy będziemy chcieli z owej metody rozszerzającej skorzystać.

Jej wywołanie jest nawet prostsze niż domyślne OrderBy. Wystarczy jako argument podać string z nazwą właściwości. W przypadku, gdy właściwość nie istnieje, zostanie rzucony wyjątek typu ArgumentException.

1 użytkowników online, w tym zalogowanych: 0, gości: 1