What is IComparable, CompareTo function, and ArrayList.Sort()
:| Read the title above. P.S. how can we sort an arraylist from greatest to least based on one variable?
I want to know this because on the Procedural Cave Generation tutorial, there is this code bit.
// [...]
void ProcessMap() {
// [...]
List<Room> survivingRooms = new List<Room> ();
// [...]
survivingRooms.Sort ();
survivingRooms [0].isMainRoom = true;
survivingRooms [0].isAccessibleFromMainRoom = true;
ConnectClosestRooms (survivingRooms);
}
struct Coord {
public int tileX;
public int tileY;
public Coord(int x, int y) {
tileX = x;
tileY = y;
}
}
class Room : IComparable<Room> {
public List<Coord> tiles;
public List<Coord> edgeTiles;
public List<Room> connectedRooms;
public int roomSize;
public bool isAccessibleFromMainRoom;
public bool isMainRoom;
public Room() {
}
public Room(List<Coord> roomTiles, int[,] map) {
tiles = roomTiles;
roomSize = tiles.Count;
connectedRooms = new List<Room>();
edgeTiles = new List<Coord>();
foreach (Coord tile in tiles) {
for (int x = tile.tileX-1; x <= tile.tileX+1; x++) {
for (int y = tile.tileY-1; y <= tile.tileY+1; y++) {
if (x == tile.tileX || y == tile.tileY) {
if (map[x,y] == 1) {
edgeTiles.Add(tile);
}
}
}
}
}
}
public void SetAccessibleFromMainRoom() {
if (!isAccessibleFromMainRoom) {
isAccessibleFromMainRoom = true;
foreach (Room connectedRoom in connectedRooms) {
connectedRoom.SetAccessibleFromMainRoom();
}
}
}
public static void ConnectRooms(Room roomA, Room roomB) {
if (roomA.isAccessibleFromMainRoom) {
roomB.SetAccessibleFromMainRoom ();
} else if (roomB.isAccessibleFromMainRoom) {
roomA.SetAccessibleFromMainRoom();
}
roomA.connectedRooms.Add (roomB);
roomB.connectedRooms.Add (roomA);
}
public bool IsConnected(Room otherRoom) {
return connectedRooms.Contains(otherRoom);
}
public int CompareTo(Room otherRoom) {
return otherRoom.roomSize.CompareTo (roomSize);
}
}
}
Answer by Bunny83 · Jun 15, 2016 at 10:02 AM
Even @tanoshimi's answer does already answer your original question, here are a few more details.
The generic List class has a Sort method to sort the elements of the List. It comes with 4 overloads:
The first Sort overload takes no parameters at all. It does only work for element types which are comparable either by default (primitive types like int, float, string) or by implementing the IComparable<T>
interface.
In the example code you posted that's the version that is used. As you can see when you look at the documentation of IComparable, it only has one method that need to be implemented: CompareTo. This method has only one parameter and it get passed another object / element to which this object should be compared to. The method return an integer which indicates the relationship between those two objects. It basically can return 3 different values:
A value less than 0 (usually "-1")
"0" (zero)
A value greater than 0 (usually "1")
When CompareTo returns a negative value, that means the object should appear before the "other" object in the sorted list.
When CompareTo return 0 it means both objects are equal, so it doesn't care which one goes first.
When CompareTo return a positive value, that means the object should appear after the "other" object.
As i said primitive types like "int" also implement this interface and can be sorted natively. Sorting always sort from the lowest to the greatest value by default. If you want to sort the values the other way round you just have to "invert" the return value of CompareTo. This can be done by literaly inverting the value by placing a "-" infront of the return value, or by swapping the two compared objects. The second solution is what has beed used in your code example. So instead of comparing the roomSize of this room with the roomSize of the other room, he swapped the two variables.
So this:
return otherRoom.roomSize.CompareTo (roomSize);
would be the same as:
return -roomSize.CompareTo (otherRoom.roomSize);
The Sort method will internally iterate through all elements and call the CompareTo method of the elements inside the List in order to determine the order of the elements.
The other 3 overloads work the same way, but they don't require the element type to implement the IComparable interface. Instead you either have to pass a delegate (method reference) to a method that can compare two elements of your List:
private static int Compare(Room room1, Room room2)
{
return room2.roomSize.CompareTo (room1.roomSize);
}
Or you have to pass a "comparer object" which does the comparison of two elements. It's basically the same as using a delegate, but the method has to be inside an object that implements the IComparer interface. This might be useful when you have external dependencies that you need during your comparing operation. For example to sort the elements based on a threshold value. Such a comparer object can only dynamically handle the sort order by checking a bool member variable. However those are more advanced things which you only need when you want to sort the list in many different ways.
To sum up:
The IComparable interface can define the default sorting order of a certain class / struct.
Passing a IComparison delegate to the Sort method is an easy way to specify a custom sorting logic
Creating a seperate IComparer object can be used when a more advanced sorting is required.
Note: instead of explicitly creating a comparer object you can also use a lambde expression / anonymous method / closure. Those will automatically create a hidden class which serves the same purpose as a comparer object due to the ability to reference variables from the outer scope.
// passing a lambda expression which does the same as the CompareTo method above
list.Sort((a,b)=>b.roomSize.CompareTo(a.roomSize));
Answer by tanoshimi · Jun 14, 2016 at 06:17 AM
Icomparable is the interface for comparable objects and CompareTo is the function called when determining how two objects should be sorted.
Sorting simple datatypes like numbers is easy and fairly unambiguous. 15 > 7 > 2 > 0. So, if you were to sort a list containing the elements {15, 2, 7, 15, 0}, the result would be 0,2,7,15,15.
But what if you were to sort a list of a custom class of colours say: {black, red, white, blue}? Is white > black? Is dog > cat? That's why you'd need to override the CompareTo function to specify how you want the comparison to operate.
Incidentally, Arraylist is very old and not very good. I'd recommend you use generic List or native arrays if your collections are of fixed size.
Umm...makes some sense. However, I was talking about how the IComparable interface is used in the Procedural Cave Generation tutorial. How did Sebastian Lague order the numbers from greatest to least?
I was talking about how the IComparable interface is used in the Procedural Cave Generation tutorial
Uhm, you might want to re-read your own question carefully. You didn't talk anything about any tutorial or procedural cave generation. You ask a relatively clear and short question and this answer answers that question very well. If you want a more specific answer, ask a more specific question which should be a bit longer than a single line.
I have never looked at any tutorial so i have no idea what you talk about. You should at least provide some background information and at least a link to the exact section of the tutorial. That's also a question that doesn't belong to the normal UnityAnswers space. IComparable and the Sort method is ordinary .NET / $$anonymous$$ono functionality.
For plain support questions you should post your question in the help room.
I did your suggestions. And i did already put this in the help section. I could have sworn I did...
P.S. I know this may be asking too much, but I would understand the concept much better if you added sample code with comments :)
Your answer
Follow this Question
Related Questions
Timer wont work 0 Answers
Make enemy patrol without Nav Points? 0 Answers
random spawn enemy problem 0 Answers
Help with Changing the Sprite image in the Script 0 Answers
this script is missed up help? 1 Answer