Sort List by more than 1 field?

BlitzMax Forums/BlitzMax Programming/Sort List by more than 1 field?

wmaass(Posted 2010) [#1]
Hi all,

I need to be able to sort a list by more than 1 criteria. I am putting together an app that shows the order in which golfers play their shots. The order rules are different depending on the situation. For tee shots the order is by score, lowest to highest. For all other shots the order is by distance, highest to lowest. No problem here. The problem is that for tee shots where the scores are identical, I need to sort by score and then if 2 or more players have the same score, sort by name. Here is what I have:


Type player

	Global what_order:Int

	Field playernum:Int
	Field name:String
	Field distance:Float
	Field distrounded:Int
	Field strokes:Int
	Field inhole:Int
	Field donetee:Int
	
		
	Method Compare:Int(Other:Object)
		If other=Null Return 0
		
		If what_order=0 '	this means (for use) 'order bades on VALUE field
			If distance = player(other).distance Return 0
			If distance < player(Other).distance Then Return 1 Else Return -1
		Else	
                        'order based on strokes field
			If strokes = player(other).strokes Return 0
			If strokes > player(other).strokes Then Return 1 Else Return -1		
		End If
	End Method

End Type



Does anyone have any tips for this?


ziggy(Posted 2010) [#2]
Override the compare method of the classes bein sorted to take into account as much fields as you wish.

A simple example to see how sorting works with lists (it is the same with arrays)
Strict
Type TMyClass
	Field One:String
	Field Two:String
	
	'This should return 1 if the withobject is smaller, 0 if both are the same and -1 if it is bigger:
	Method Compare:Int(withObject:Object)
		
		'If the class is compared with a different one:
		If TMyClass(withObject) = Null Then Return Super.Compare(withObject)
		
		'If the class is compared with a 2 fields sortable class:
		Local Comparer:TMyClass = TMyClass(withObject)
		If Comparer.One < One Then Return 1
		If Comparer.One > One Then Return - 1
		If Comparer.Two > Two Then Return - 1
		If Comparer.Two < Two Then Return 1
		Return 0
	End Method
	
	'Just a simple debug ToString function:
	Method ToString:String()
		Return ("One = " + One + ", Two = " + Two)
	End Method
	
End Type


'We fill a list with random instances of the class, with random values on the fields one and two
Local List:TList = New TList
For Local i:Int = 0 To 40
	Local Class:TMyClass = New TMyClass
	Class.One = Chr(Rand(65, 67))
	Class.two = Chr(Rand(65, 80)) + Chr(Rand(65, 80)) + Chr(Rand(65, 80)) + Chr(Rand(65, 80))
	List.AddLast(Class)
Next


'We show the contents:
Print "Unsorted:"
For Local Class:TMyClass = EachIn List
	Print Class.Tostring()
Next

Print "Sorted:"
List.Sort()
For Local Class:TMyClass = EachIn List
	Print Class.Tostring()
Next



wmaass(Posted 2010) [#3]
Thanks ziggy, checking it out now.


Oddball(Posted 2010) [#4]
As an addition do not override the Compare method. If you do you're just opening yourself up to a whole world of pain as Sort is not the only function that uses the Compare method. Declare a custom compare function to go along with the SortList function.

SortList list,True,CustomCompare

Function CustomCompare:Int( o1:Object, o2:Object )
	'Compare code here
End Function



ziggy(Posted 2010) [#5]
As an addition do not override the Compare method. If you do you're just opening yourself up to a whole world of pain as Sort is not the only function that uses the Compare method.
I've never had problems with overloading the compare method when needed. So all in all, I'm very curious to know wich methods could give problems with this, just to be aware.
Also, how can you sort then an array?


Oddball(Posted 2010) [#6]
The Remove methods and functions use the Compare method. They will remove the first entry that fits the criteria, which if you have overridden the Compare method might not be the object you intended. If you are not using lists and only using arrays then overriding Compare should be ok. Just be warned.


ziggy(Posted 2010) [#7]
I thought that was solved. Anyway, thanks a lot, I wasn't aware of this.

[EDIT] I've been looking at the source code of linkedlists and, as long as you're using unique fields for sorting (you don't keep duplicates on the lists) there are not any potential issues overriding the compare method. Otherwise, the method will remove (or find) the first instance that compareas as 'equal'.


wmaass(Posted 2010) [#8]
By "using unique fields" do you mean that I might run into a problem unless I create a custom compare function as Oddbal suggests? Since the "strokes" field in my type are often have the same value.


Tommo(Posted 2010) [#9]
You can replace "Return 0" with "Return super.compare(Other)" to avoid that problem.


wmaass(Posted 2010) [#10]
Thanks for the tips guys, it is working great now.


ziggy(Posted 2010) [#11]
@Tommo: Thanks! That's the easiest way to prevent any problem with the overridden compare method. How I wish this was documented...