1

I have an ArrayList where I store 10 objects with two random generated numbers each and I want to sort the list by the Y values of the objects.

Random rndm = new Random();
ArrayList CustomList = new ArrayList();

for (int i = 0; i < 10; i++) 
{
    Point LP = new Point(rndm.Next(50), rndm.Next(50));
    CustomList.Add(LP); 
}

PrintList(CustomList);
CustomList.Sort(); 
PrintList(CustomList);

The Sort method throws the following exception:

System.InvalidOperationException: The comparer threw an exception. ---> System.ArgumentException: At least one object must implement IComparable.

I assume it doesn't work because the Sort method can not handle two dimensional objects in the ArrayLists. How can I get the Sort method to work now?

2
  • 3
    Why not use List<Point> instead of ArrayList? Commented Aug 26, 2015 at 20:04
  • How were you expecting the Sort method to know you wanted to sort by Y and not X? Use List<point> and list = list.OrderBy(t=>t.Y).ToList();. You shouldn't be using ArrayList anymore, now that there are nice generic collections like List<T>. Commented Aug 26, 2015 at 20:10

3 Answers 3

4
ArrayList CustomList = new ArrayList();

is just a one dimensional list - the fact that a Point has two dimensions has nothing to do with the dimensionality of the list. A two-dimensional array of Points (Point[][]) would look like this:

row col1  col2  col3
1   Point Point Point
2   Point Point Point
3   Point Point Point

The problem is that Point does not implement the IComparable interface by default. This makes sense - can you tell if (1, 3) is greater than or smaller than (2, 2)? If you compare by first coordinate, it is smaller, but if you compare by distance to origin, it is greater.

Now that you have decided which comparison function you want to use, you can create a new PointComparer class:

public class PointComparer : IComparer<Point> {
    // return -1 if x is the smallest, 1 if y is the smallest, 0 if they're equal
    public int Compare(Point x, Point y) {
        return x.Y.CompareTo(y.Y);
    }
}

declare your CustomList as

List<Point> CustomList = new List<Point>();

and sort it with

CustomList.Sort(new PointComparer());
Sign up to request clarification or add additional context in comments.

2 Comments

There was no real answer possible to the original question. I have included an answer to the new version of the question.
return x.Y.Compare(y.Y); need to be return x.Y.CompareTo(y.Y);
2

The best way would be to use List<Point>, still even with ArrayList you can use LINQ:

Random rndm = new Random();
ArrayList CustomList = new ArrayList();

for (int i = 0; i < 10; i++) 
{
  Point LP = new Point(rndm.Next(50), rndm.Next(50));
  CustomList.Add(LP); 
}

var sorted = CustomList.Cast<Point>()
                        .OrderBy(p => p.Y)
                        .ToList();

If you want to have ArrayList as a returned structure:

ArrayList sortedAL = new ArrayList(sorted);

Comments

1

The original poster said:

The Sort method throws the following exception:

System.InvalidOperationException: The comparer threw an exception. --->
   System.ArgumentException: At least one object must implement IComparable.

I assume it doesn't work because the Sort method can not handle two dimensional ArrayLists.

You are confused on two points:

  • First, it doesn't work because of what the exception message told you: the Point class doesn't implement the interface IComparable. That means your ArrayList doesn't know how to compare two points:

How does Point(1,2) compare to Point(2,1: is the first greater than, less than or equal to the second?

  • Second, ArrayList is just an array -- a list -- of objects. It is inherently 1-dimensional. It is an adjustable length equivalent of, say, object[].

  • Third, you should avoid using the non-generic collection classes, for a couple of reasons.

    1. They're not type-safe, and
    2. The generic versions are a whole lot easier to use since they're strongly typed: you don't have to downcast the contained references into the correct type.

So, given this definition of Point:

public struct Point
{
  public int X { get ; set ; }
  public int Y { get ; set ; }

  public Point( int x , int y )
  {
     this.X = x ;
     this.Y = y ;
  }
}

You could sort your ArrayList thusly:

public class PointComparer : IComparer
{
  int IComparer.Compare( object x , object y )
  {
    if ( x != null && !(x is Point) ) throw new ArgumentException();
    if ( y != null && !(y is Point) ) throw new ArgumentException();

    if      ( x == null && y == null ) return  0 ; // two nulls are equals
    else if ( x == null && y != null ) return -1 ; // we're collating nulls-low: change to +1 to collate nulls-high
    else if ( x != null && y == null ) return +1 ; // we're collating nulls-low: change to -1 to collate nulls-high
    else // both x and y are non-null
    {
      Point left  = (Point) x ;
      Point right = (Point) y ;

      if ( left.Y <  right.Y ) return -1 ;
      else if ( left.Y >  right.Y ) return +1 ;
      else // ( left.Y == right.Y )
      {
        if      ( left.X  < right.X )    return -1 ;
        else if ( left.X  > right.X )    return +1 ;
        else /* ( left.X == right.X ) */ return  0 ;
      }

    }
  }
}

...

ArrayList listOfPoints = CreateListofPoints();
listOfPoints.Sort( new PointComparer() ) ;

Or you could use the generic collections and accomplish the same thing a whole lot more concisely:

List<Point> listOfPoints = CreateListOfPoints();
listOfPoints.Sort( (left,right) => left.Y.CompareTo(right.Y) != 0
                                 ? left.Y.CompareTo(right.Y )
                                 : left.X.CompareTo(right.X)
) ;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.