Using Icomparer for Sorting

Using IComparer for sorting

You need to implement the strongly type interface (MSDN).

public class CoordinatesBasedComparer : IComparer<Point>
{
public int Compare(Point a, Point b)
{
if ((a.x == b.x) && (a.y == b.y))
return 0;
if ((a.x < b.x) || ((a.x == b.x) && (a.y < b.y)))
return -1;

return 1;
}
}

BTW, I think you use too many braces, I believe they should be used only when they contribute to the compiler. This is my version:

if (a.x == b.x && a.y == b.y)
return 0;
if (a.x < b.x || (a.x == b.x && a.y < b.y))
return -1;

Just like I dislike people using return (0).


Note that if you target a .Net-3.5+ application you can use LINQ which is easier and even faster with sorting.

LINQ vesion can be something like:

var orderedList = Points.OrderBy(point => point.x)
.ThenBy(point => point.y)
.ToList();

How to sort a list of objects with IComparable and IComparer

First, there's no need to have an IComparer<Employee> that sorts by descending if your Employee class implements IComparable<Employee> using the same sort criteria. And it's horribly inefficient for your Employee class to instantiate a new IComparer<Employee> for every comparision.

You should change your Employee class so that its CompareTo looks like this:

int CompareTo(Employee next)
{
return next.NumberOfKids.CompareTo(this.NumberOfKids);
}

Then you can ditch the EmployeeComparer altogether and sort like this:

list = list.Take(3).ToList();
list.Sort(); // Uses default IComparable for the Employee class
return list;

Typically, you make the IComparable<T> implementation on the class perform the default sorting order. In the case of employees, that'd probably either be by employee ID or perhaps last name, first name. IComparer<T> implementations should be for other sorting criteria.

With List<T>, though, you have another option: use an anonymous function. For example, you could do this by writing:

list.Sort((x, y) => y.NumberOfKids.CompareTo(x.NumberOfKids));

See this List.Sort overload.

Or, you could just ditch the whole idea of IComparer<T> and IComparable<T> and List.Sort altogether and do it the LINQ way:

var result = list.Take(3).OrderByDescending(x => x.NumberOfKids).ToList();

How does icomparer implementing class decide sorting order on the array items?

Array.Sort or any other .Sort variations in .NET sort items in ascending order.

When you implement your comparer, the meaning of the result is very simple:

  • less than 0: x less than y
  • greater than 0: x greater than y
  • equals 0: x equals y
  • comparison operation must be transitive, e.g. 1 ≤ 4 ≤ 8.

The order of comparing items in a set depends on a sorting algorithm, so no specific order of Compare calls is guaranteed.

Having IComparer<> is a good abstraction, and can be useful in other scenarios, not for sorting only.

If you want any specific order of items, you can either define your rules of comparer that preserves transitivity or perhaps not using .Sort.


UPDATE

As a programmer, you should think:

public int Compare(int x, int y)
{
// OPTION 1
if (x < y)
return -1; // X is less than Y, place X before Y - acsending order
...

// OPTION 2
if (x < y)
return 1; // X is less than Y, place X after Y - descending order
...

return 0; // keep order of X and Y if possible (stable comparison only)
}

UPDATE 2

OK, imagine we have a comparer defined like this:

interface IComparer<T>
{
SortPlacement Compare(T x, T y);
}

Where SortPlacement is defined like this:

enum SortPlacement
{
Try_To_Keep_Same_Order_Of_X_and_Y = 0,
Place_X_Before_Y_Ascending_Order = -1, // or any negative number
Place_X_After_Y_Descending_Order = 1, // or any positive number
}

When you implement you comparer you are super clear:

public SortPlacement Compare(int x, int y)
{
// OPTION 1
if (x < y)
return SortPlacement.Place_X_Before_Y_Ascending_Order;
...

// OPTION 2
if (x < y)
return SortPlacement.Place_X_After_Y_Descending_Order;
...

// OPTION 3
return SortPlacement.Try_To_Keep_Same_Order_Of_X_and_Y;
}

Now forget about silly enumertion type and replace it with integer numbers - that is a simple convention.

Using IComparer for grouping and sorting

For the specific example I would do the following.

var sorted = list.GroupBy(x => x.A)
.OrderByDescending(g => g.Max(x => x.B))
.SelectMany(g => g.OrderByDescending(x => x.B));

You basically need to group on A first then order the groups on the max value of B, then order the items in each group on B.

Another option would be to first order on B, then group on A, then order the groups on the value of B in the first item in the group (which should be the max B for the group since we already ordered on them), then just flatten the results.

var sorted = list.OrderByDescending(x => x.B)
.GroupBy(x => x.A)
.OrderByDescending(g => g.First().B)
.SelectMany(g => g);

C# Using IComparer to sort x number of columns

You need the sort to be stable. If the one You use is not, use another, linq provides one on IEnumerable for instance. I admit it means quite big changes to the code, as You need to sort outside of datagridview and only assign the result. Btw comparing values by string representation is far from perfect for numbers.

EDIT

I somehow overlooked that it is unbound. If You want to go this way, You can do it like this:

private class GridSort : System.Collections.IComparer
{
List<GridSortData> ColIndexSorts = new List<GridSortData>();

public GridSort(List<GridSortData> ColIndexSorts)
{
this.ColIndexSorts = ColIndexSorts;
}

public int Compare(object x, object y)
{
FormGridRow FirstComparable = (FormGridRow)x;
FormGridRow SecondComparable = (FormGridRow)y;

for (int i = 0; i < ColIndexSorts.Count; ++i)
{
int index = ColIndexSorts[i].ColumnSortIndex;
object a = FirstComparable.Cells[index].Value;
object b = SecondComparable.Cells[index].Value;
int result = a.ToString().CompareTo(b.ToString());
if (result != 0)
{
if (ColIndexSorts[i].SortOrder == SortOrder.Ascending)
{
return result;
}
else
{
return -result;
}
}
}

return 0;
}
}

You have to set SortMode for all columns to programmatic and handle ColumnHeaderMouseClick, but I guess You already know that.

Sorting an ArrayList by using IComparer Compare Function C#

You'd better use the IComparable<> interface.

"The object to be sorted will implement IComparable while the class that is going to sort the objects will implement IComparer."

Source: difference between IComparable and IComparer

public class Point : IComparable<Point>
{
public int x;
public int y;

public Point(int x_Point, int y_Point)
{
x = x_Point;
y = y_Point;
}

public int CompareTo(Point other)
{
if (this.x == other.x && this.y == other.y)
return 0;
if (this.y < other.y)
return -1;
if (this.y == other.y && this.x < other.x)
return -1;
return 1;
}
}

public static void Main()
{
var AL = new List<Point>(); // ditch the ArrayList for good... ;-)
Random R = new Random();
for (int i = 0; i < 10; i++)
{
Point p = new Point(R.Next(50), R.Next(50));
AL.Add(p);
}
PrintValues(AL);
AL.Sort();
PrintValues(AL);

}

Generic IComparer for sorting different objects in different properties

Use an interface and use generic IComparer Interface instead of IComparer

public interface IObjectWithNameProperty
{
string Name {get; set;}
}

public class MyNameComparer : IComparer<IObjectWithNameProperty>
{
public int Compare(IObjectWithNameProperty x, IObjectWithNameProperty y)
{
...
}
}

public class Car: IObjectWithNameProperty
{
public string Name {get;set;}
...
}
public class Dog: IObjectWithNameProperty
{
public string Name {get;set;}
...
}


Related Topics



Leave a reply



Submit