.NET Tip #13: Vlastní řazení objektů v kolekci

Tomáš Herceg       12. 10. 2008       .NET Tips       7168 zobrazení

Představte si, že máte kolekci nějakých objektů (např. List) a potřebujete je setřídit podle nějakých složitějších kritérií. Tak mějme například kolekci objektů s informacemi o zaměstnancích a chceme je setřídit podle data narození tak, že budeme ignorovat rok, ale vezmem v úvahu jen měsíc a den (sestavujeme třeba firemní narozeninový kalendář).

Je jasné, že si na to můžeme najít nějaký třídící algoritmus a napsat jej s příslušnou třídící logikou sami, ale je to naprosto zbytečné a je to jen způsob, jak do aplikace dostat blbé chyby. Daleko lepší je využít obecné třídění, které .NET Framework umí a naimplementovat tomuto objektu rozhraní IComparable.

''' <summary>
''' Zaměstnanec
''' </summary>
Public Class Employee
    Implements IComparable

    Private _name As String
    ''' <summary>
    ''' Jméno zaměstnance
    ''' </summary>
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Private _birthDate As Date
    ''' <summary>
    ''' Datum narození
    ''' </summary>
    Public Property BirthDate() As Date
        Get
            Return _birthDate
        End Get
        Set(ByVal value As Date)
            _birthDate = value
        End Set
    End Property

    ''' <summary>
    ''' Výpis objektu na text
    ''' </summary>
    Public Overrides Function ToString() As String
        Return String.Format("{0,-50} {1:d}", Me.Name, Me.BirthDate)
    End Function

    ''' <summary>
    ''' Vlastní způsob řazení
    ''' </summary>
    Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
        ' vytáhnout měsíc a den
        Dim month As Integer = CType(obj, Employee).BirthDate.Month
        Dim day As Integer = CType(obj, Employee).BirthDate.Day

        ' porovnat měsíce
        If month < Me.BirthDate.Month Then
            Return 1
        ElseIf month > Me.BirthDate.Month Then
            Return -1
        Else
            ' pokud jsou měsíce stejné, porovnat dny
            Return Me.BirthDate.Day.CompareTo(day)
        End If
    End Function
End Class

Všimněte si hlavně řádku Implements IComparable, což je implementace rozhraní. Pomocí ní kolekci List říkáme, že objekty lze třídit a metodou CompareTo porovnáváme, který je větší a menší.

Metoda by měla porovnat objekt obj, který dostaneme v parametru, a instanci, na které se provádí, čili Me. Předaný objekt si přetypujeme na Employee a vytáhneme si z něj měsíc a den. Pokud má aktuální instance měsíc menší než objekt předaný, pak vrátíme 1, pokud menší, vrátíme –1. Pokud je měsíc stejný, musíme třídit podle dne. Tady si můžeme opět porovnání napsat, anebo použijeme standardní CompareTo na typ Integer a ten čísla porovná stejným způsobem, jako bychom to napsali my. Samozřejmě bychom mohli i měsíce třídit pomocí CompareTo, ale chtěl jsem ukázat obě možnosti. Lepší implementace je tedy tato:

    ''' <summary>
    ''' Vlastní způsob řazení
    ''' </summary>
    Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
        ' vytáhnout měsíc a den
        Dim month As Integer = CType(obj, Employee).BirthDate.Month
        Dim day As Integer = CType(obj, Employee).BirthDate.Day

        ' setřídit podle měsíce
        Dim i As Integer = Me.BirthDate.Month.CompareTo(month)
        ' pokud jsou měsíce stejné, třídit podle dne
        If i = 0 Then i = Me.BirthDate.Day.CompareTo(day)

        Return i
    End Function

A ukázkový příklad použití? Naplníme si seznam List(Of Employee) testovacími daty a pak zavoláme na seznamu jednoduše Sort. Seznam si pomocí vestavěného mechanismu zjistí, jestli je datový typ tříditelný (implementuje IComparable) a pokud ano, tak jej setřídí. Pak jednoduše objekty vypíšeme popořadě na konzoli.

Module Module1

    Sub Main()

        'vstupní data
        Dim employees As New List(Of Employee)
        employees.Add(New Employee() With {.Name = "Lukáš", .BirthDate = #3/28/1978#})
        employees.Add(New Employee() With {.Name = "Martin", .BirthDate = #5/4/1964#})
        employees.Add(New Employee() With {.Name = "Tomáš", .BirthDate = #2/20/1959#})
        employees.Add(New Employee() With {.Name = "Jan", .BirthDate = #8/18/1985#})
        employees.Add(New Employee() With {.Name = "Lubomír", .BirthDate = #11/12/1986#})
        employees.Add(New Employee() With {.Name = "Edmund", .BirthDate = #5/1/1973#})
        employees.Add(New Employee() With {.Name = "Břetislav", .BirthDate = #1/14/1982#})
        employees.Add(New Employee() With {.Name = "Jitka", .BirthDate = #7/19/1975#})
        employees.Add(New Employee() With {.Name = "Jiří", .BirthDate = #12/17/1981#})
        employees.Add(New Employee() With {.Name = "Luboš", .BirthDate = #6/1/1980#})

        'seřadit je pomocí výchozího Compareru
        employees.Sort()

        'vypsat zaměstnance
        For Each e In employees
            Console.WriteLine(e.ToString())
        Next
        Console.ReadLine()
    End Sub

End Module

Setříděné objekty

Pokud chcete třídit objekty podle více různých kritérií (jednou podle posledního písmena z jména a jindy podle data narození), IComparable už nestačí. Stejně tak není možné použít ho v případě, kdy chceme třídit vestavěné .NET Frameworkové objekty (třeba FileInfo), do kterých si metody nemůžeme jen tak přidávat.

Pak je vhodné pro každý typ třídění napsat vlastní třídu a implementovat rozhraní IComparer. Pak můžete do každé z nich napsat libovolnou metodu Compare. Pak při volání metody Sort předáte jako druhý parametr novou instanci této třídy. To poskytuje lepší možnosti třídění, můžete například třídě přidat pár vlastností a během třídění je vzít v úvahu. Zcela jistě to zvládnete sami.

Jinak IComparable i IComparer mají i generické varianty, které můžete použít rovněž a nebudete musetpřetypovávat parametry z typu Object.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

Diskuse: .NET Tip #12: Vlastní řazení objektů v kolekci

No ale to je mooc hezký a názorný příklad. Přesně tohle jsem potřeboval.

Díky!

Petr

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback