.NET IComparer sort error - c#

I have run into a very weird issue recently. I deployed a new version of a program and am receiving this error when the IComparer.Compare() method gets called internally:
Unable to sort because the IComparer.Compare0 method returns inconsistent
results. Either a value does not compare equal to itself, or one value repeatedly
compared to another value yields different results. x:",x's type: 'String',
IComparer.".
The odd thing is that I cannot reproduce this issue on my computer. It doesn't happen for me in Visual Studio 2013 (debug or release versions) and it doesn't happen when I install the application either. To make things weirder, it doesn't even happen on every computer in production, only about 30% of them.
My application targets .NET Framework 4 and the platform target is x86.
There is only one instance of an IComparer object in my code, here it is:
public int Compare(string stringOne, string stringTwo)
{
if (stringOne == stringTwo) { return 0; }
else if (stringOne == null) { return stringTwo == null ? 0 : -1; }
else if (stringTwo == null) { return stringOne == null ? 0 : 1; }
else if (stringOne.StartsWith("_") && !stringTwo.StartsWith("_"))
{
return -1;
}
else if (!stringOne.StartsWith("_") && stringTwo.StartsWith("_"))
{
return 1;
}
else if ((stringOne.StartsWith("l") || stringOne.StartsWith("L")) &&
(!stringTwo.StartsWith("l") || !stringTwo.StartsWith("L")))
{
return -1;
}
else if ((!stringOne.StartsWith("l") || !stringOne.StartsWith("L")) &&
(stringTwo.StartsWith("l") || stringTwo.StartsWith("L")))
{
return 1;
}
else
{
if (stringTwo == null) { return 1; }
else { return stringOne.CompareTo(stringTwo) == 1 ? -1 : 1; }
}
}
Has anyone else had this issue and found a solution to it? Does my comparer look it covers all cases? I am totally lost about this issue and have no idea what to try next. Any help will be greatly appreciated.

This
else if ((stringOne.StartsWith("l") || stringOne.StartsWith("L")) &&
(!stringTwo.StartsWith("l") || !stringTwo.StartsWith("L")))
{
return -1;
}
else if ((!stringOne.StartsWith("l") || !stringOne.StartsWith("L")) &&
(stringTwo.StartsWith("l") || stringTwo.StartsWith("L")))
{
return 1;
}
should be
else if ((stringOne.StartsWith("l") || stringOne.StartsWith("L")) &&
!(stringTwo.StartsWith("l") || stringTwo.StartsWith("L")))
{
return -1;
}
else if (!(stringOne.StartsWith("l") || stringOne.StartsWith("L")) &&
(stringTwo.StartsWith("l") || stringTwo.StartsWith("L")))
{
return 1;
}
As a side note, the way you wrote this comparer function is highly ineffecient.

Related

Issue with Unity Update Loop

I was playing a little in Unity on a project and I stumbled upon an issue I can't address. Please keep in mind I am a beginner, and my understanding of Unity is fairly limited.
So the issue is this..
I wanted to test some if statement that went like this:
void Update()
{
if (isRow1Good() || isRow2Good() || isRow3Good() || isRow4Good() || isRow5Good() ||
isRow6Good() || isRow7Good() || isRow8Good() || isRow9Good() || isRow10Good())
{
Debug.Log("LOL");
}
}
The content of the functions is this:
Piece p1 = row1[0].ReturnPiece();
Piece p2 = row1[1].ReturnPiece();
Piece p3 = row1[2].ReturnPiece();
Piece p4 = row1[3].ReturnPiece();
if (p1.isTall && p2.isTall && p3.isTall && p4.isTall)
{
return true;
}
else if (p1.isRed && p2.isRed && p3.isRed && p4.isRed)
{
return true;
}
else if (p1.isHollow && p2.isHollow && p3.isHollow && p4.isHollow)
{
return true;
}
else if (p1.isCylinder && p2.isCylinder && p3.isCylinder && p4.isCylinder)
{
return true;
}
else
{
return false;
}
And the others are the same, just instead of row1[] it's row2[].
If the first function is true, the console logs the "LOL" message, but if the second or the third and so on are true, the value is not getting outputted. I tried changing the functions' places, every time it only cares if the first one is true, and the rest are ignored.
What would you say I am doing wrong? :D
else if (p1.isRed && p2.isRed && p3.isRed && p4.isRed)
{
return true;
}
else if (p1.isHollow && p2.isHollow && p3.isHollow && p4.isHollow)
{
return true;
}
else if (p1.isCylinder && p2.isCylinder && p3.isCylinder && p4.isCylinder)
{
return true;
}
if I'm understanding you correct if all four pieces in a row are red its not returning true. You need to double check if the 'isRed' is set properly. if p1.isRed and p2.isRed and p3.isRed and p4.isRed then it will return true no matter what. Also double check if you meant to put || instead of &&.

How to overcome the ArgumentException while sorting using the IComparer interface in C#?

I'm sorting an array of RowRanges using the IComparer. It works fine when the row range is upto 16. Beyond 16, the exception occurs stating that, sorting is not performed and the exception is thrown stating that Soting cannot be performed, because the values compared to itself and a value is compared repeatedly to other. While debugging , I found that the issue occurs during objects taken for for comparison. But how is that exception occurs after 16, while it is working fine upto 16? Please refer to the below code snippet.
internal class ColComparer : IComparer
{
public int Compare(object a, object b)
{
if (a == null || b == null)
{
return System.Collections.Comparer.Default.Compare(a, b);
}
GridRangeInfo r0 = (GridRangeInfo)a;
GridRangeInfo r1 = (GridRangeInfo)b;
if (r0 == r1)
{
return 0;
}
else
{
if (r0.RangeType == GridRangeInfoType.Table)
{
return -1;
}
else if (r1.RangeType == GridRangeInfoType.Table)
{
return 1;
}
else if (r0.RangeType == GridRangeInfoType.Rows)
{
return -1;
}
else if (r1.RangeType == GridRangeInfoType.Rows)
{
return 1;
}
else if (r0.Left != r1.Left)
{
return r0.Left - r1.Left;
}
else if (r0.Right != r1.Right)
{
return r0.Right - r1.Right;
}
else if (r0.Top != r1.Top)
{
return r0.Top - r1.Top;
}
else
{
return r0.Bottom - r1.Bottom;
}
}
}
}
class SortArray
{
//Array Data
//
GridRangeInfo[] ranges = new GridRangeInfo[this.Count];
Array.Sort(ranges, new GridRangeInfo.ColComparer());
}
Let me know where the exception occurs and share your ideas to resolve this.
Thanks in Advance,
Sindhu
The conditions such as r0.RangeType == GridRangeInfoType.Table .. return -1 produce a non-stable result. This is because it depends on "which" item is r0 and r1.
Follow the same if-then-return pattern as with the rest of the function - where r0.X is compared to r1.X and then 'something is done based on that'.
eg.
if (r0.RangeType != r1.RangeType) {
{
if (r0.RangeType == GridRangeInfoType.Table) {
return -1; // r0 first - prioritize 'Table' range types
} else {
return 1; // r1 first
}
}

Counting in lists with conditional statements

I know this is a common topic but I have been through most of the threads here on Stackoverflow and having followed them, can't see to get mine to satisfy all conditions.
I want to return the 2nd item in a list and if the list is null or only has 1 item in it, return 0;
I have this:
public int practice(List<int> items)
{
if (items == null)
{
return 0;
}
else if (items.Count == 1)
{
return 0;
}
else
{
int second_place = items[1];
return second_place;
}
}
I can't get this to work if the list has only 1 item in it. It just bypasses my else if condition and then fails.
I have tried items.Count and items.Count() but it doesn't seem to make a difference.
Instead of adding another else condition, you could just combine them as follows:
public int practice(List<int> items)
{
if (items == null || items.Count <= 1)
{
return 0;
}
else
{
int second_place = items[1];
return second_place;
}
}
Ok so I figured out what I wasn't doing correct. The code wasn't passing if the list had 0 items in it (but was not null).
So I added another else if statement to handle that:
else if (items.Count == 0)
{
return 0;
}
And then it passed. I didn't originally do this because I had not initially thought of the case where the list is not null but has 0 items in it. I was incorrectly thinking it had either a value of null or 1 item or greater.

Why my C# code is causing a Stack Overflow

This is the code giving a stack overflow it only happens about half the time and i have no idea why it's doing it. From what I seen it only happens with the Coms(TopCom, etc) are in a mass of numbers so around 5+ then it stack overflows.
public bool getConnected(int d) {
if (topCom.connection != null) {
if (d != topCom.connection.id) {
if (topCom.connection.id == 0) {
return true;
} else if (topCom.connection.connected == true) {
if (Development.instance.currentDos.buttons[topCom.connection.id].getConnected(id)) {
return true;
}
}
}
}
if (leftCom.connection != null) {
if (d != leftCom.connection.id) {
if (leftCom.connection.id == 0) {
return true;
} else if (leftCom.connection.connected == true) {
if (Development.instance.currentDos.buttons[leftCom.connection.id].getConnected(id)) {
return true;
}
}
}
}
if (rightCom.connection != null) {
if (d != rightCom.connection.id) {
if (rightCom.connection.id == 0) {
return true;
} else if (rightCom.connection.connected == true) {
if (Development.instance.currentDos.buttons[rightCom.connection.id].getConnected(id)) {
return true;
}
}
}
}
if (botCom.connection != null) {
if (d != botCom.connection.id) {
if (botCom.connection.id == 0) {
return true;
} else if (botCom.connection.connected == true) {
if (Development.instance.currentDos.buttons[botCom.connection.id].getConnected(id)) {
return true;
}
}
}
}
return false;
}
This happens in recursive functions where you don't have a base condition for ending the recursion. You basically keep calling the function until you reach stack overflow.. Trace your code through and figure out why it calls itself endlessly.
The fact that people here can't really tell what you're trying to accomplish is a code smell of sorts.
A big part of that is the fact that you have an incredible amount of nesting in your code. Nested conditionals increase the difficulty of debugging code, as you're discovering now. Additionally, you could easily combine some of your conditionals - all of your conditionals in any top-level branch can actually be combined into one statement, as follows:
if ((topCom.connection != null && d != topCom.connection.id && topCom.connection.id == 0) ||
(topCom.connection.connected == true &&
Development.instance.currentDos.buttons[topCom.connection.id].getConnected(id)))
{
return true;
}
return false;
As far as I can imagine, there's no point in having separate conditional branches that perform the same function, e.g. if (a) { return true; } else if (b) { return true; }. Just move the logic from else if into the original if conditional.
However, I'd recommend encapsulating some or all of this logic into a separate function, given that it seems like you're performing the same logic on each of your connections. You could create a function like so:
public bool ConnectionIsValid(connectionObject // replace with the actual type)
{
if (topCom.connection != null && d != topCom.connection.id && topCom.connection.id == 0)
|| (topCom.connection.connected == true
&& Development.instance.currentDos.buttons[topCom.connection.id].getConnected(id))
return true;
return false;
}
So that you could then just call ConnectionIsValid on each of your connections, rather than using 80-some lines on conditionals for each connection.
It also seems doubtful that there's a StackOverflowException occurring in this code. Unless you have a circular reference related to any of the objects referenced in this code (in which case, there's a decent chance you used a setter accessor to assign a value to the same variable:
object A
{
set
{
this.A = value;
}
}
which will always cause a stack overflow, it's likely you've introduced some sort of recursion outside the scope of the included code.

Can anyone simplify this Algorithm for me?

Basically I just want to check if one time period overlaps with another.
Null end date means till infinity. Can anyone shorten this for me as its quite hard to read at times. Cheers
public class TimePeriod
{
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public bool Overlaps(TimePeriod other)
{
// Means it overlaps
if (other.StartDate == this.StartDate
|| other.EndDate == this.StartDate
|| other.StartDate == this.EndDate
|| other.EndDate == this.EndDate)
return true;
if(this.StartDate > other.StartDate)
{
// Negative
if (this.EndDate.HasValue)
{
if (this.EndDate.Value < other.StartDate)
return true;
if (other.EndDate.HasValue && this.EndDate.Value < other.EndDate.Value)
return true;
}
// Negative
if (other.EndDate.HasValue)
{
if (other.EndDate.Value > this.StartDate)
return true;
if (this.EndDate.HasValue && other.EndDate.Value > this.EndDate.Value)
return true;
}
else
return true;
}
else if(this.StartDate < other.StartDate)
{
// Negative
if (this.EndDate.HasValue)
{
if (this.EndDate.Value > other.StartDate)
return true;
if (other.EndDate.HasValue && this.EndDate.Value > other.EndDate.Value)
return true;
}
else
return true;
// Negative
if (other.EndDate.HasValue)
{
if (other.EndDate.Value < this.StartDate)
return true;
if (this.EndDate.HasValue && other.EndDate.Value < this.EndDate.Value)
return true;
}
}
return false;
}
}
public bool Overlaps(TimePeriod other)
{
return (other.StartDate >= StartDate &&
(EndDate == null || other.StartDate <= EndDate.Value)) ||
(StartDate >= other.StartDate &&
(other.EndDate == null || StartDate <= other.EndDate.Value))
}
How about this one:
public bool Overlaps(TimePeriod other)
{
bool isOtherEarlier = this.StartDate > other.StartDate;
TimePeriod earlier = isOtherEarlier ? other : this;
TimePeriod later = isOtherEarlier ? this : other;
return !earlier.EndDate.HasValue || earlier.EndDate > later.StartDate;
}
Check this out: DateTimeOverlaps
Very generally, if all variables are nullable datetimes, then
return (StartA.HasValue? StartA.Value:DateTime.Minimum) <=
(EndB.HasValue? EndB.Value:DateTime.Maximum) &&
(EndA.HasValue? EndA.Value:DateTime.Maximum) >=
(StartB.HasValue? StartB.Value:DateTime.Minimum);
The concept, (as explained in link) is very simple, and is simply and concisely expressed above.
If the start is before the others end, and the end is after the other start, you have overlap. This says it all, and all that is necessary, in one simple sentence with two clauses, and whatever code you write should concisely map to that simple concept without obfuscating it. Adding extra unecessary complexity does not add understanding, it only adds length.
Fail Case 1: TopStart AFTER other End - Fail
|----------|
|--|
Fail Case 2: TopEnd AFTER other start - Fail
|-----------|
|------|
In all other cases, start is before other end, and end is after other start.
case A
|----------|
|-----|
case B
| ---------|
|-------------------|
case C
|-----------|
|------|
case D
|-----------|
|-------------|
Any time you're dealing with pure boolean logic, you can distill your algorithm down to a single statement. But don't assume that just because you can, you should. Unless performance is vital, always go for readable code over compact code. (Not that compactness == performance, necessarily)
This is easy to read because it's comprised entirely of single AND expressions, and it's obvious that they all determine a non-overlap:
public bool Overlaps(TimePeriod other)
{
if (other.EndDate.HasValue && other.EndDate < StartDate)
return false;
if (EndDate.HasValue && EndDate < other.StartDate)
return false;
if (!EndDate.HasValue && other.EndDate < StartDate)
return false;
if (!other.EndDate.HasValue && EndDate < other.StartDate)
return false;
return true;
}
Not that the other answers are bad (I like Adam's; his formatting is obviously designed to aid readability). I'm just saying this because it's clear you're a beginner, and I think this is one lesson that isn't heeded enough (I'm guilty). Somebody (I think Martin Fowler) once said something like: "Any fool can write code that a computer understands, but a good programmer can write code that a human understands."

Categories