Reputation: 89
I'm trying to rearrange a list of animals in order from medium, large, small. I've been trying to do this with IComparable.CompareTo but I can't figure out how to order it in this specific way. I can only find ways to order by Ascending or Descending values.
My enum:
public enum AnimalSize
{
Small = 1, Medium = 3, Large = 5,
}
My Animal class:
public class Animal : IComparable<Animal>
{
public bool IsCarnivore;
public AnimalSize Size;
public Animal(bool isCarnivore, AnimalSize size)
{
this.IsCarnivore = isCarnivore;
this.Size = size;
}
public int CompareTo(Animal other)
{
return this.Size.CompareTo(other.Size);
}
I call the CompareTo from another class where I have a private list of animals.
How can I arrange the animals in order of Medium, Large, Small?
Upvotes: 2
Views: 2330
Reputation: 22896
You can check if any of them are Medium before comparing (not tested) :
public int CompareTo(Animal other)
{
if (this.Size == other.Size) return 0;
if (this.Size == AnimalSize.Medium) return -1;
if (other.Size == AnimalSize.Medium) return 1;
return this.Size.CompareTo(other.Size);
}
A bit more efficient without branch misprediction (3 becomes 1, 5 ⇒ 3, and 1 ⇒ 7) :
public int CompareTo(Animal other) => ((int)this.Size + 2 ^ 4) - ((int)other.Size + 2 ^ 4);
or lookup array (3 ⇒ 1, 5 ⇒ 2, and 1 ⇒ 3) :
byte[] order = { 0, 3, 0, 1, 0, 2 };
public int CompareTo(Animal other) => order[(byte)this.Size] - order[(byte)other.Size];
Upvotes: 0
Reputation: 52280
I would not implement IComparable<T>
for a non-scalar object such as an animal. What if you want to sort by height instead of size? Or by name? IComparable implies that the object can be converted to a one-dimensional quantity.
Instead, define the sort order you need in an array, and use a simple LINQ query to sort.
public static void Main()
{
var sampleData = new List<Animal>
{
new Animal(false, AnimalSize.Small),
new Animal(false, AnimalSize.Large),
new Animal(false, AnimalSize.Medium)
};
AnimalSize[] customSortOrder = new[]
{
AnimalSize.Small,
AnimalSize.Medium,
AnimalSize.Large
};
var results = sampleData.OrderBy( a => Array.IndexOf(customSortOrder, a.Size ));
foreach (var a in results)
{
Console.WriteLine(a.Size);
}
Output:
Small
Medium
Large
Upvotes: 12
Reputation: 441
If you want to use IComparable, you'll need to convert to int first.
public int CompareTo(Animal other)
{
return ((int)this.Size).CompareTo((int)other.Size);
}
With this change, calling the Sort() function of a list containing animal objects will work as expected.
Upvotes: 0
Reputation: 460360
You could create a custom Comparer<Animal>
:
public class AnimalSizeComparer : Comparer<Animal>
{
private readonly IList<AnimalSize> _order;
public AnimalSizeComparer():this(Enum.GetValues(typeof(AnimalSize)).Cast<AnimalSize>().ToArray())
{
}
public AnimalSizeComparer(IList<AnimalSize> order)
{
_order = order;
}
public override int Compare(Animal x, Animal y)
{
if (x == null && y == null) return 0;
if (x == null) return -1;
if (y == null) return 1;
return _order.IndexOf(x.Size).CompareTo(_order.IndexOf(y.Size));
}
}
You initialize it with your sample order in this way:
var comparer = new AnimalSizeComparer(new[] { AnimalSize.Medium, AnimalSize.Large, AnimalSize.Small });
yourAminalList.Sort(comparer);
Upvotes: 1