IComparable sort problem

S

SimeonArgus

I need to sort a list of points, so I've considered using an
IComparable implementation. Should be easy, right? But I need to know
two things in the CompareTo function, not one.

Test1: I need to know the distance in space from the "current point"
to the point sent in. This is what the current IComparable interfeace
gives me.

Test2 : I also need to know the distance in space from the "previous
point" to the point being sent in. HERE is my problem. How can I get
this?

Return the integer of (Test1 - Test2)

// Code version of the above statement in the class SpacePoint...
int IComparable<SpacePoint>.CompareTo(SpacePoint other)
{
int T1 = this.DistanceFrom( other );

int T2 = this.DistanceFrom( ??? );

return (T1 - T2);
}


The best, of course, would be to extend CompareTo to be
CompareTo(SpacePoint other, SpacePoint Prev)... but I can't think of a
clean way to do this. Any suggestions would be appreciated.

--Sim
 
N

Nicholas Paldino [.NET/C# MVP]

Sim,

The IComparable interface isn't the right solution here. The only way
it would work is if your class had a notion of a previous point, which I
feel it probably doesn't (and would probably mess up your class design if it
did).

What you might want to do is create a class that contains your previous
point, a manager class of some kind, and then implement the IComparer
interface on it, which will take two of your class types and compare them
(much in the same way IComparable works, but this sits outside the class
implementation). Most places that use IComparable will take IComparer
implementations as well.

Then, on your manager class, you can have it store the previous
instance, and then take the two instances to compare in your IComparer
implementation.

Hope this helps.
 
P

PS

SimeonArgus said:
I need to sort a list of points, so I've considered using an
IComparable implementation. Should be easy, right? But I need to know
two things in the CompareTo function, not one.

Test1: I need to know the distance in space from the "current point"
to the point sent in. This is what the current IComparable interfeace
gives me.

when you create the comparer you pass the current point in the constructor.
Then that point is available within your comparer object to use as you need.
Test2 : I also need to know the distance in space from the "previous
point" to the point being sent in. HERE is my problem. How can I get
this?

The previous point sounds like it is not constant soI am not sure what you
are wanting to do here. Please eloborate.

PS
Return the integer of (Test1 - Test2)

// Code version of the above statement in the class SpacePoint...
int IComparable<SpacePoint>.CompareTo(SpacePoint other)
{
int T1 = this.DistanceFrom( other );

int T2 = this.DistanceFrom( ??? );

return (T1 - T2);
}


The best, of course, would be to extend CompareTo to be
CompareTo(SpacePoint other, SpacePoint Prev)... but I can't think of a
clean way to do this. Any suggestions would be appreciated.

--Sim

Whe
 
S

SimeonArgus

YEP!!! That's it.

I didn't know the ICompar_ER_ class existed. =) I already had a
manager class, so this is PERFECT!

Thank you.

--Sim
 
S

SimeonArgus

YEP!!! That's it.

I didn't know the ICompar_ER_ class existed. =) I already had a
manager class, so this is PERFECT!

Thank you.

--Sim


Okay.. new problem, same situation. When I call
PointManager.SortByDistance(), once every few thousand records (I hate
these kind of bugs) this crashes with an error:

An unhandled exception of type 'System.ArgumentException' occurred in
mscorlib.dll

Additional information: IComparer (or the IComparable methods it
relies upon) did not return zero when Array.Sort called x.
CompareTo(x). x: 'TSPoint #85' x's type: 'TSPoint' The IComparer:
'System.Array+FunctorComparer`1[PointManager.TSPoint]'.

Note, that this doesn't happen with every call. Not even with every
list of points I'm trying to sort. Only once in a while, and
generally on larger lists of points.

Can anyone review what I've done and tell me where I'm being a moron?
I don't see the error.

Here's the code:

public void SortByDistance()
{
this.Sort(this.CompareDistance);
}

#region IComparer<TSPoint> Members
protected int CompareDistance(TSPoint x, TSPoint y)
{
if ((x == null) && (y == null))
return 0;

if (x == null)
return -1;

if (y == null)
return 1;

double D2 = new TSPath(this).DistanceMiles(x);
double D1 = new TSPath(this).DistanceMiles(y);

if (D1 > D2) return 1;
if (D1 < D2) return -1;
if (D1 == D2) return 0;

return 0; // to prevent compile issues.
}
#endregion
 
S

SimeonArgus

YEP!!! That's it.
I didn't know the ICompar_ER_ class existed. =) I already had a
manager class, so this is PERFECT!
Thank you.

Okay.. new problem, same situation. When I call
PointManager.SortByDistance(), once every few thousand records (I hate
these kind of bugs) this crashes with an error:

An unhandled exception of type 'System.ArgumentException' occurred in
mscorlib.dll

Additional information: IComparer (or the IComparable methods it
relies upon) did not return zero when Array.Sort called x.
CompareTo(x). x: 'TSPoint #85' x's type: 'TSPoint' The IComparer:
'System.Array+FunctorComparer`1[PointManager.TSPoint]'.

Note, that this doesn't happen with every call. Not even with every
list of points I'm trying to sort. Only once in a while, and
generally on larger lists of points.

Can anyone review what I've done and tell me where I'm being a moron?
I don't see the error.

Here's the code:
<SNIP>

I've tweaked the code, and still getting the error. New Code:

public void SortDistance()
{
this.Sort(this.CompareDistance);
}

#region IComparer<TSPoint> Members
protected int CompareDistance(TSPoint x, TSPoint y)
{
if (x == y) return 0; // <--- Added, JUUUST to be sure.
Still crashes.

if ((x == null) && (y == null))
return 0;

if (x == null)
return 0;

if (y == null)
return 0;

double D2 = new TSPath(this).DistanceMiles(x);
double D1 = new TSPath(this).DistanceMiles(y);

if (D1 > D2) return 1;
if (D1 < D2) return -1;
if (D1 == D2) return 0;

return 0; // to prevent compile issues.
}
#endregion
 
B

Bill Butler

First:
What exactly is the problem that you are attempting to solve?
At first I assumed that you were attempting to sort a list of points in
ascending distance order from a FixedPoint. But that doesn't mesh with
your hypothetical CompareTo method

int IComparable<SpacePoint>.CompareTo(SpacePoint other)
{
int T1 = this.DistanceFrom( other );
int T2 = this.DistanceFrom( ??? );
return (T1 - T2);
}

Because if you were attempting to sort a list of points in ascending
distance order from a FixedPoint it would look like this

int IComparable<SpacePoint>.CompareTo(SpacePoint other)
{
int T1 = this.DistanceFrom( FixedPoint);
int T2 = other.DistanceFrom( FixedPoint);
return (T1 - T2);
}

So...once again..How are you attempting to sort these points?


Second:

You mention IComparable and IComparer, but your code shows neither.
It would be helpful if you (at a minimum) provided a complete IComparer
class for examination.
It looks like the CompareDistance method is ACTING like the Compare
method on and IComparer....but I don't actually see it called anywhere.
It might also be nice to know what TSPoint and TSPath are since
DistanceMiles relies on both of them.


Third:
Could you post a short (but complete) version of this that will
reproduce the problem.
Obviously a set of datapoints that trigger the problem would be nice as
well


Bill


SimeonArgus said:
YEP!!! That's it.
I didn't know the ICompar_ER_ class existed. =) I already had a
manager class, so this is PERFECT!
Thank you.

Okay.. new problem, same situation. When I call
PointManager.SortByDistance(), once every few thousand records (I
hate
these kind of bugs) this crashes with an error:

An unhandled exception of type 'System.ArgumentException' occurred in
mscorlib.dll

Additional information: IComparer (or the IComparable methods it
relies upon) did not return zero when Array.Sort called x.
CompareTo(x). x: 'TSPoint #85' x's type: 'TSPoint' The IComparer:
'System.Array+FunctorComparer`1[PointManager.TSPoint]'.

Note, that this doesn't happen with every call. Not even with every
list of points I'm trying to sort. Only once in a while, and
generally on larger lists of points.

Can anyone review what I've done and tell me where I'm being a moron?
I don't see the error.

Here's the code:
<SNIP>

I've tweaked the code, and still getting the error. New Code:

public void SortDistance()
{
this.Sort(this.CompareDistance);
}

#region IComparer<TSPoint> Members
protected int CompareDistance(TSPoint x, TSPoint y)
{
if (x == y) return 0; // <--- Added, JUUUST to be sure.
Still crashes.

if ((x == null) && (y == null))
return 0;

if (x == null)
return 0;

if (y == null)
return 0;

double D2 = new TSPath(this).DistanceMiles(x);
double D1 = new TSPath(this).DistanceMiles(y);

if (D1 > D2) return 1;
if (D1 < D2) return -1;
if (D1 == D2) return 0;

return 0; // to prevent compile issues.
}
#endregion
 
D

D. Yates

Sim,

I don't think your problem is not in your comparer.

I think the System.ArgumentException points to a problem in here:
double D2 = new TSPath(this).DistanceMiles(x);
double D1 = new TSPath(this).DistanceMiles(y);


By the way, try this:
1. Drop a listbox (listbox1) on a form
2. Drop a button on the form and hook it up to button_Click


private void button_Click(object sender, EventArgs e)
{
List<DaveData> myList = new List<DaveData>();
myList.Add(null);
myList.Add(null);
myList.Add(null);
myList.Add(null);
myList.Add(new DaveData(3));
myList.Add(new DaveData(5));
myList.Add(null);
myList.Add(null);
myList.Add(null);
myList.Add(new DaveData(1));
myList.Add(new DaveData(4));
myList.Add(null);
myList.Add(null);
myList.Add(null);

myList.Sort(new DaveSorter());

listBox1.Items.Clear();
foreach(DaveData i in myList)
{
if (i != null)
listBox1.Items.Add(i.Value.ToString());
else listBox1.Items.Add("Null item");
}
}


public class DaveData
{
private int _value;
public DaveData(int someNumber)
{
Value = someNumber;
}

public int Value
{
get { return _value; }
set { _value = value; }
}
}

public class DaveSorter : IComparer<DaveData>
{
public int Compare(DaveData x, DaveData y)
{
if ((x == null) && (y == null))
return 0;

if (x == null)
return 0;

if (y == null)
return 0;

// if (x == null)
// return -1;
//
// if (y == null)
// return 1;


if (x.Value == y.Value)
return 0;

if (x.Value > y.Value)
return -1;

return 1;
}
}


Dave
 
S

SimeonArgus

I need to sort a list of points, so I've considered using an
IComparable implementation. Should be easy, right? But I need to know
two things in the CompareTo function, not one.

Test1: I need to know the distance in space from the "current point"
to the point sent in. This is what the current IComparable interfeace
gives me.

Test2 : I also need to know the distance in space from the "previous
point" to the point being sent in. HERE is my problem. How can I get
this?

Return the integer of (Test1 - Test2)

// Code version of the above statement in the class SpacePoint...
int IComparable<SpacePoint>.CompareTo(SpacePoint other)
{
int T1 = this.DistanceFrom( other );

int T2 = this.DistanceFrom( ??? );

return (T1 - T2);

}

The best, of course, would be to extend CompareTo to be
CompareTo(SpacePoint other, SpacePoint Prev)... but I can't think of a
clean way to do this. Any suggestions would be appreciated.

--Sim



I found the problem... kind-of.

I was trying to exploit the "Sort"function to find the best possible
route from point A to point Q. The problem was that the MS engine was
trying to build a truth table on the data. The problem was that if you
took Route "A -> B -> C...->Q" the Comparison of B to C was that B
made the smallest route.

Okay.. skip forward five-thousand itterations. It has now started at
"L". L -> A -> F -> C -> B ... -> Q.

In this case, C makes the smallest route. Now, looking at all of the
comparison histories, you'll end up with C < B < C, which is a
mathmatical impossibility. That's what caused the Sort function to
crash. Be caureful of using Sort for purposes it was never intended to
handle.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top