IComparable and IComparer

mandelbrot

Centurion
Joined
Jul 1, 2005
Location
UK North East
Dear All,


I've been looking into the usage of the comparison interfaces over the past few days, as I need to sort and compare a particular class. My problem lies with the fact that, though I can compare the objects using the correct property, sorting requires the use of a different property.

As the data for the classes is directly taken from a database I compare the record's unique ID (SQL Server identity field) with either; the target equivalent class' id, or the specified integer. Each record also contains a SortOrder field, identifying its position in the list.

So, do I need to create an independent class to use as a comparer to sort my classes, or can I implement the interface within the current class but somehow trap what's needed to produce either a comparison or sort...

Alternatively, am I just losing the point somewhere?... :confused:


Paul.
 

HJB417

Contributor
Joined
Mar 5, 2003
Location
Lowell, MA
the former... make an icomparer that compares the record id, another icomparer that comapres the class id and another that compares the sort order.

The reason why is, if you have a bunch of objects stored in an arraylist, you can just pass the icomparer instance to the arraylist.sort method.
 

mandelbrot

Centurion
Joined
Jul 1, 2005
Location
UK North East
I must be doing something wrong here. I've written the code and embedded it as a private class in my ICollection class...
Visual Basic:
...
...
    'Perform search for specific title in collection...
    Public Function GetCodeByName(ByVal pName As String) As sysCode
        Return itemArray(Array.BinarySearch(itemArray, compTitle))
    End Function
...
...
    'Comparer class for codes...
    Private Class CodeTitleComparer
        Implements IComparer

        'Compare function as implemented by the interface...
        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
            'Check that we're dealing with sysCode objects...
            If Not TypeOf x Is sysCode Or Not TypeOf y Is String Then
                Throw New InvalidCastException("Expected sysCode object")
            End If
            'Check the codes...
            Return String.Compare(CType(x, sysCode).Name, y.ToString)
        End Function

    End Class
The only trouble is that the BinarySearch result is always -17! Am I doing something wrong? Have I missed something?


Paul.
 

mandelbrot

Centurion
Joined
Jul 1, 2005
Location
UK North East
Do I need to sort the items, in that case, by whichever search I'm performing (i.e. if I'm doing a search on the title, the array needs to be sorted by the title etc)?
 

mandelbrot

Centurion
Joined
Jul 1, 2005
Location
UK North East
Ok, this is the code I've written to check for a particular title within the array of the current container class - it's fairly essential to the operation of the class...
Visual Basic:
Public Class sysCode
...
    Dim itemArray(15) As sysCode
...
    Private compId As New CodeIdComparer
    Private compTitle As New CodeTitleComparer
    Private compValue As New CodeValueComparer
...
    'Gets a child from the current collection...
    Public Function GetCodeByName(ByVal pName As String) As sysCode
        Array.Sort(itemArray, compTitle)
        Dim temp As Int32 = Array.BinarySearch(itemArray, pName, compTitle)
        Console.Write(temp)
    End Function
...
    'Comparer class for codes...
    Private Class CodeComparer
        Implements IComparer
        'Compare function as implemented by the interface...
        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
            'Check that we're dealing with sysCode objects...
            If Not TypeOf x Is sysCode Or Not TypeOf y Is sysCode Then
                Throw New InvalidCastException("Expected sysCode object")
            End If
            'Check the codes...
            Return CType(x, sysCode).Id.CompareTo(CType(y, sysCode).Id)
        End Function
    End Class
    'Comparer class for titles...
    Private Class CodeTitleComparer
        Implements IComparer
        'Compare function as implemented by the interface...
        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
            'Check that we're dealing with sysCode objects...
            If Not TypeOf x Is sysCode Or Not TypeOf y Is String Then
                Throw New InvalidCastException("Expected sysCode and String object")
            End If
            'Check the codes...
            Return String.Compare(CType(x, sysCode).Name, y.ToString)
        End Function
    End Class

End Class
The code above shows the basics for the search that I'm doing. For the sake of testing, I've replaced the actual return of the code object with a console.write of the returned value.

As you can see, the array gets sorted by the title, but still I'm getting a result of -17. I can't for the life of me see where it's going wrong! I feel I'm missing something basic....

If you can point out what's wrong I'd really appreciate it.


Many thanks,
Paul.
 

Diesel

Contributor
Joined
Aug 18, 2003
Im wondering...since itemArray is class scope...where do you initialize each object?

Anyhow, i don't see how it's sorting...

Visual Basic:
 'Check that we're dealing with sysCode objects...
            If Not TypeOf x Is sysCode Or Not TypeOf y Is String Then
                Throw New InvalidCastException("Expected sysCode and String object")
            End If
            'Check the codes...
            Return String.Compare(CType(x, sysCode).Name, y.ToString)

You are checking for a sysCode object and a string. When sorting, you will only be getting syscode objects...
 

mandelbrot

Centurion
Joined
Jul 1, 2005
Location
UK North East
Yep - I was thinking about that this morning on the way to work, and realised that. I should be comparing the titles of two sysCode objects - I'm just off to try it now.

Many thanks Diesel,

Paul.
 

mandelbrot

Centurion
Joined
Jul 1, 2005
Location
UK North East
My appologies for returning to this thread, but I've been away on holiday and now I'm back! Had a great time in Gran Canaria if you're interested, but anyway...

I've now moved on from the previous entry and have defined two classes for each search condition. The first sorts the class, the second allows a binary search given the appropriate type. But it's still not working - it's still returning a negative value. I've written separate classes now to deal with sorting and searching. Each one is applied in the appropriate command.

The code below belongs to a class called sysCode which provides a very useful look-up facility that allows multiple attributes for different codes. Originally I used a simple For...Next loop to loop through and obtain the searched for objects - a simple method so I could get the whole class working before I implemented more complex stuff. Now I'm looking at implementing IComparer, but just cannot get it to work!
Visual Basic:
Implements ICollection, IComparable
...
...
Private itemArray() As sysCode              'Accepts only code objects
Private arrayPointer As Integer = -1        'Pointer to the current object
Private arrayElements As Integer            'The number of elements in the array
Private defaultCollectSize As Integer = 16  'The default number of elements in the collection

Private sortId As New CodeIdSort
Private sortTitle As New CodeTitleSort
Private sortValue As New CodeValueSort
Private compId As New CodeIdComparer
Private compTitle As New CodeTitleComparer
Private compValue As New CodeValueComparer
...
...
'Compare...
Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
    If TypeOf obj Is sysCode Then
        Return _Id.CompareTo(CType(obj, sysCode).Id)
    ElseIf TypeOf obj Is Int32 Then
        Return _Id.CompareTo(DirectCast(obj, Int32))
    ElseIf TypeOf obj Is String Then
        Return _Name.CompareTo(obj.ToString)
    Else
        Throw New InvalidCastException("sysCode, Int32, Integer, String or Date required.")
    End If
End Function
...
...
Public Function GetCodeByName(ByVal pName As String) As sysCode
    Dim tempId As Integer
    'There's no need to sort if we've only got 1 or less elements...
    If arrayPointer > 0 Then
        Array.Sort(itemArray, 0, arrayPointer, sortTitle)
        tempId = Array.BinarySearch(itemArray, 0, arrayPointer, pName, compTitle)
        If tempId >= 0 Then Return itemArray(tempId)
    End If
End Function
...
...
'Comparer class for titles...
Private Class CodeTitleSort
    Implements IComparer
    'Compare function as implemented by the interface...
    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
        'Check that we're dealing with sysCode objects...
        If Not TypeOf x Is sysCode Or Not TypeOf y Is sysCode Then
            Throw New InvalidCastException("Expected sysCode object")
        End If
        'Check the codes...
        Return DirectCast(x, sysCode).Name.CompareTo(DirectCast(y, sysCode).Name)
    End Function
End Class

'Comparer class for sorting titles...
Private Class CodeTitleComparer
    Implements IComparer
    'Compare function as implemented by the interface...
    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
        'Check that we're dealing with sysCode and string objects...
        If Not TypeOf x Is sysCode Or Not TypeOf y Is String Then
            Throw New InvalidCastException("Expected sysCode and String object")
        End If
        'Check the codes...
        Return String.Compare(DirectCast(x, sysCode).Name, y.ToString)
    End Function
End Class
...
...
As always - any pointers will be greatly appreciated.


Paul.
 

MrPaul

Contributor
Joined
Jun 28, 2006
Location
Hampshire, UK
Debugging is a useful skill!

I recently had a problem with a custom compare function. I think you need to do some basic debugging here - output the contents of the array after it has been sorted (providing it is not too large), so you can eliminate that as being the problem. Then, in the CodeTitleComparer class, output the names of the two items being compared (and the result), so you can track the progress of the binary search function.

Good luck :cool:
 

mandelbrot

Centurion
Joined
Jul 1, 2005
Location
UK North East
Right - I've sorted it!

After several days messing around with other problems I've come back to it (always a wise thing to do!) and noticed where I've gone wrong!

The answer is fairly straightforward, when you stand back and look. How does the container (or array, I suppose) know what it's sorting on? The answer is not the current class, but the object that is stored within the array. Because I am using nested classes I was referring directly to the comparers defined within my current class, whereas I should have been looking at the codes within the array. Simple, really, but when you're tying yourself up in knots with tree structures things can become a little bleary!

I'd like to thank you all for your direction and help.


Anyway, have a good weekend, plenty of beer, and (for those in the UK) enjoy the sun!


Paul. ;)
 
Top Bottom