Where is my IComparable implementation going wrong?

namespace SortableLists { using System; using System.Collections.Generic; public class Program { private static void Main() { var list = new List<ListItem> { new ListItem {AdmissionCode = "801r", Name = "Rajesh Koothrappali", RollNumber = 54}, new ListItem {AdmissionCode = "892k", Name = "Leonard Leakey Hofstadter", RollNumber = 34}, new ListItem {AdmissionCode = "1203a", Name = "Sheldon Lee Cooper", RollNumber = 46}, new ListItem {AdmissionCode = "802x", Name = "Howard Wolowitz", RollNumber = 98} }; list.ForEach(x => Console.WriteLine(x.RollNumber + ","+x.Name + "," + x.AdmissionCode)); Console.Write("\n"); list.Sort(); list.ForEach(x => Console.WriteLine(x.RollNumber + "," + x.Name + "," + x.AdmissionCode)); Console.ReadKey(); } } public class ListItem : IComparable<ListItem> { public int RollNumber { get; set; } public string Name { get; set; } public string AdmissionCode { get; set; } #region Implementation of IComparable<in ListItem> public int CompareTo(ListItem other) { return AdmissionCode.CompareTo(other.AdmissionCode); } #endregion } } 

I don’t know what sorting is, where is the reception code 1203 Dr. Sheldon appears at the top of the list after sorting ??? I was expecting 801 802 803 and 1203 ... can anyone explain?

+4
source share
5 answers

The numbers you are comparing are not considered as numbers, but as strings! And with strings, the letter "1" precedes "8", so a larger number appears first, because when it is processed as text, the order is different.

I recommend that you convert this field to int if you want to treat it as one.

Edit : for your edited question (the field also contains letters) you will need to write your own comparison logic in order to compare them in the desired order.

For example, I think you want the logic to be like this:

  • Divide the code into numbers and letters.
  • Compare numbers only (like int).
  • If the partial numbers are the same for both values, compare the rest as a string.

Embed this logic (or any other logic that you really want) in your CompareTo method and you will have the desired order.

The code for this logic might be:

 public class ListItem : IComparable<ListItem> { public int RollNumber { get; set; } public string Name { get; set; } public string AdmissionCode { get; set; } private static readonly char[] Numbers = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; #region Implementation of IComparable<in ListItem> public int CompareTo(ListItem other) { // Assumes AdmissionCode is in ####ABC format, // with at least one number and any amount of letters. string myNumberPart, myRemainingPart; string otherNumberPart, otherRemainingPart; SplitAdmissionCode(AdmissionCode, out myNumberPart, out myRemainingPart); SplitAdmissionCode(other.AdmissionCode, out otherNumberPart, out otherRemainingPart); int myNumber = int.Parse(myNumberPart); int otherNumber = int.Parse(otherNumberPart); int result = myNumber.CompareTo(otherNumber); // Numbers are different. if (result != 0) return result; // Numbers are same. Use text compare for the remaining part. return myRemainingPart.CompareTo(otherRemainingPart); } private void SplitAdmissionCode(string code, out string numbersPart, out string remainingPart) { int lastNumberIndex = code.LastIndexOfAny(Numbers); numbersPart = code.Substring(0, lastNumberIndex + 1); if (lastNumberIndex == code.Length - 1) remainingPart = ""; else remainingPart = code.Substring(lastNumberIndex + 1); } #endregion } 
+4
source

You are sorting strings, not numbers. The CompareTo line does not consider length.

EDIT (for your edited question): when sorting lines starting with numbers, sorting CompareTo methods CompareTo unlikely to produce the expected results, since all it does is the alphabet.

+3
source

String "1203" is less than "801". You can try converting strings to numbers before comparing (if they represent the values ​​of numbers substantially)

0
source

This is the expected functionality, as others have noted.
If you want an order like 801r, 802x, 892k, 1203a to do this

 public class ListItem : IComparable<ListItem> { public int RollNumber { get; set; } public string Name { get; set; } public string AdmissionCode { get; set; } public int CompareTo(ListItem other) { return ExtractNumbers(this.AdmissionCode).CompareTo(ExtractNumbers(other.AdmissionCode)); } private int ExtractNumbers(string expr) { return Convert.ToInt32(String.Join(null,System.Text.RegularExpressions.Regex.Split(expr, "[^\\d]"))); } } 
0
source

If you want to keep it simple:

 public class ListItem : IComparable<ListItem> { public int RollNumber { get; set; } public string Name { get; set; } public string AdmissionCode { get; set; } #region Implementation of IComparable<in ListItem> public int CompareTo(ListItem other) { return this.AdmissionCode.Length != other.AdmissionCode.Length ? this.AdmissionCode.Length.CompareTo(other.AdmissionCode.Length) : this.AdmissionCode.CompareTo(other.AdmissionCode); } #endregion } 
0
source

Source: https://habr.com/ru/post/1335049/


All Articles