IComparable CompareTo(), How can I implement comparison on 2 different fields

StackOverflow https://stackoverflow.com/questions/21989823

  •  15-10-2022
  •  | 
  •  

سؤال

I Have a class like so :

public class Incident : IComparable<Incident>
{
    public int Id { get; set; }
    public string Description { get; set; }
    public string IncidentType { get; set; }

    public int CompareTo(Incident other)
    {
        string str = other.Description;
        int ret = -1;
        if (String.Compare(Description, str, StringComparison.OrdinalIgnoreCase) < 0)
            ret = 1;
        else if (String.Compare(Description, str, StringComparison.OrdinalIgnoreCase) > 0)
            ret = -1;
        else if (String.Compare(Description, str, StringComparison.OrdinalIgnoreCase) == 0)
            ret = 0;

        return ret;
    }
}

I can sort objects of this class alphabetically based on the description field. How can I sort the class based on IncidentType field ?

I do not want to sort them on both fields simultaneously. Sometimes I want to sort incidents by Description, sometimes on IncidentType

هل كانت مفيدة؟

المحلول

If it's a typical case for you to sort either by Description or by IncidentType, you can implement different Comparers to sort on different conditions

public class Incident: IComparable<Incident> {
  ...

  private IncidentTypeComparerClass: IComparer<Incident> {
    public int Compare(Incident x, Incident y) {
      if (Object.ReferenceEquals(x, y))
        return 0;
      else if (Object.ReferenceEquals(x, null))
        return -1;
      else if (Object.ReferenceEquals(null, y))
        return 1;

      return String.Compare(x.IncidentType, y.IncidentType, StringComparison.OrdinalIgnoreCase);
    }    
  }

  // Additional comparer, by IncidentType 
  public static readonly ByIncidentTypeComparer: IComparer<Incident> = new IncidentTypeComparerClass();

  ...
}

...

List<Incident> incidents = ...

// Sort by IncidentType    
incidents.Sort(Incident.ByIncidentTypeComparer);
...
// Default sort (that's by Description)
incidents.Sort();

In case you want to sort by IncidentType only once or twice you can do it explicitly:

List<Incident> incidents = ...

incidents.Sort((x, y) => String.Compare(x.IncidentType, 
                                        y.IncidentType, 
                                        StringComparison.OrdinalIgnoreCase));

نصائح أخرى

You can implement IComparer<T> in seperate class and you can use this comparer when you sorting your elements:

public class IncidentComparer : IComparer<Incident>
{
    public int Compare(Incident x, Incident y)
    {
        return x.IncidentType.CompareTo(y.IncidentType);
    }
}

For example if you have a list like this, you can use your comparer:

List<Incident> incidents = new List<Incident>();
...
incidents.Sort(new IncidentComparer());

Or alternatively you can use LINQ instead:

incidents = incidents.OrderBy(x => x.IncidentType).ToList();

You can return a value earlier than the end of a function, so you can have an if statement the checks if ret is 0 and returns ret if it's true. If it's not true, the if statement will be skipped and the rest of the function will be evaluated. For example:

public class Incident : IComparable<Incident>
{
    public int Id { get; set; }
    public string Description { get; set; }
    public string IncidentType { get; set; }

    public int CompareTo(Incident other)
    {
        string str = other.Description;
        int ret = String.Compare(str, Description, StringComparison.OrdinalIgnoreCase);
        if (ret != 0)
            return ret;
        str = other.IncidentType;
        ret = String.Compare(str, IncidentType, StringComparison.OrdinalIgnoreCase);
        return ret;
    }
}

will sort by Description, and if the Descriptions are the same, then it will sort by IncidentType. If you want sorting by IncidentType first, switch the two.

public int CompareTo(Incident other)
    {
        string str = other.IncidentType;
        int ret = -1;
        if (String.Compare(IncidentType, str, StringComparison.OrdinalIgnoreCase) < 0)
            ret = 1;
        else if (String.Compare(IncidentType, str, StringComparison.OrdinalIgnoreCase) > 0)
            ret = -1;
        else if (String.Compare(IncidentType, str, StringComparison.OrdinalIgnoreCase) == 0)
            ret = 0;

        return ret;
    }

Remember, this is just a plain string comparison on IncidentType. You might have to introduce more complex business rules here.

To sort, if you have for example:

List<Indicent> listIncident = new List<Incident>(); 

//This to sort based on the incidentType
listIndicent = listIncident.OrderBy(x => x.IncidentType).ToList();

//This to sort based on the description
listIndicent = listIncident.OrderBy(x => x.Description).ToList();

//This to sort based on two
listIndicent = listIncident.OrderBy(x => x.Description).ThenBy(x => x.IncidentType).ToList();
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top