mandelbrot Posted June 19, 2006 Posted June 19, 2006 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. Quote
HJB417 Posted June 19, 2006 Posted June 19, 2006 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. Quote
mandelbrot Posted June 19, 2006 Author Posted June 19, 2006 Thanks for your reply HJB, Ok, so let me see if I've got this right: I create an independant comparer class and, on sorting the array list, apply it?... Quote
mandelbrot Posted June 20, 2006 Author Posted June 20, 2006 I must be doing something wrong here. I've written the code and embedded it as a private class in my ICollection class... ... ... '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. Quote
Administrators PlausiblyDamp Posted June 20, 2006 Administrators Posted June 20, 2006 Does it work if you do an IndexOf rather than a binary search? Also is the itemArray sorted correctly? Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
mandelbrot Posted June 20, 2006 Author Posted June 20, 2006 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)? Quote
Administrators PlausiblyDamp Posted June 20, 2006 Administrators Posted June 20, 2006 BinarySearch requires the data to be in a sorted order, if the data isn't sorted then the results are meaningless. Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
mandelbrot Posted June 20, 2006 Author Posted June 20, 2006 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... 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. Quote
Diesel Posted June 22, 2006 Posted June 22, 2006 Im wondering...since itemArray is class scope...where do you initialize each object? Anyhow, i don't see how it's sorting... '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... Quote
mandelbrot Posted June 22, 2006 Author Posted June 22, 2006 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. Quote
mandelbrot Posted July 19, 2006 Author Posted July 19, 2006 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! 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. Quote
MrPaul Posted July 19, 2006 Posted July 19, 2006 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: Quote Never trouble another for what you can do for yourself.
mandelbrot Posted July 21, 2006 Author Posted July 21, 2006 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. ;) Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.