paul-2011
paul-2011

Reputation: 704

Sorting a custom object list

I have a List<> containing object from a class which has a String property that I want to sort using the following rule:

  1. All objects inside the list which the "code" is X5 should be on top of the list.
  2. All objects inside the list which the "code" is G2 will be after the X5 objects.
  3. All objects inside the list which the "code" is H3 will be after the G2 objects.
  4. All other object should be at the end of the list, sorted alphabetically by the "code".

I tried some code but kept throwing this exception:

System.ArgumentException: Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: ''.

Code attempt:

public class myObject: IComparable<myObject>
{
   int id { get; set; }
   string name { get; set; }
   string code { get; set; }

   public myObject()
   {
   }

   public int CompareTo(myObject other)
   {
       //I don't know how to write the sort logic
   }
}

Later I would like to create a list of "myObject" and sort it:

List<myObject> objects = new List<myObject>();
objects.Add(new MyObject() { id = 0, name = "JENNIFER"; code = "G2"; });
objects.Add(new MyObject() { id = 1, name = "TOM"; code = "H3"; });
objects.Add(new MyObject() { id = 2, name = "JACK"; code = "G2"; });
objects.Add(new MyObject() { id = 3, name = "SAM"; code = "X5"; });
objects.Sort();

And the list would be in the following order:

  1. SAM
  2. JENNIFER
  3. JACK
  4. TOM

Upvotes: 2

Views: 242

Answers (3)

jmcilhinney
jmcilhinney

Reputation: 54457

Store your codes in an array in the order you specify. You can then sort first on the Array.IndexOf the object code values.

int result;

result = Array.IndexOf(codes, this.code).CompareTo(Array.IndexOf(codes, other.code));

if (result == 0)
{
    result = // Compare next property.
}

// etc.

return result;

Just note that this is not a full solution but just relating to the codes that are prioritised over the others. You'd first have to make sure that both codes were in the array and handle the situation where they weren't separately.

Upvotes: 1

Hamlet Hakobyan
Hamlet Hakobyan

Reputation: 33381

If it is necessary to implement IComparable you can use above answer. But you can do it in-place.

objects.OrderBy(
         o => o.code == "X5" ? 1 : o.code == "G2" ? 2 : o.code == "H3" ? 3 : 4);

If you want ordering in group (i.e. G2) you can add ThenBy as well.

Upvotes: 4

Jon
Jon

Reputation: 437734

What you want to do is keep a map of "special codes" to their relative weights (that determine sort order among them). Then, when comparing objects:

  • if both codes are "special" the comparison result is that of comparing their weights
  • if only one is special then the object having it comes first ("is smaller")
  • if none are special, the comparison result is code1.CompareTo(code2)

This will allow you to maintain the code very easily in the future (if you need to add more special codes or change the weights of those you already have).

So, the code would look somewhat like this:

private static readonly Dictionary<string, int> weights =
new Dictionary<string, int>()
{
    { "X5", 1 },
    { "G2", 2 },
    { "H3", 3 }
}

public int CompareTo(myObject other)
{
    var thisIsSpecial = weights.ContainsKey(this.code);
    var otherIsSpecial = weights.ContainsKey(other.code);

    if (thisIsSpecial && otherIsSpecial)
    {
        return weights[this.code] - weights [other.code];
    }
    else if (thisIsSpecial)
    {
        return -1;
    }
    else if (otherIsSpecial)
    {
        return 1;
    }

    return this.code.CompareTo(other.code);
}

Of course the code needs some improvement (there are no checks for null, it might be a good idea to have a separate comparer instead of hardcoding a static map into the object class, property names should not be all lowercase) but the idea will always be the same.

Upvotes: 2

Related Questions