Can anyone simplify this Algorithm for me? - c#

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."

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 &&.

Is this kind of a bool method a bad practice?

Sometimes I find myself writing a bool method that looks like this:
public bool isRunning()
{
if (!(move == Moving.None) && staminaRegan == true)
{
if (keyState.IsKeyDown(Keys.Space))
{
EntityAnimation.interval = 10;
return true;
}
else
{
EntityAnimation.interval = 65;
return false;
}
}
else
{
EntityAnimation.interval = 65;
return false;
}
}
(This is XNA by the way) As you can see, I have a bool isRunning in which I made an if statement where Im checking if (Player is moving) && (regains stamina, which is set to false once stamina reaches value lesser than 6.0f)
and then I simply check if Space is pressed, if yes then my Animation is faster(the smaller the interval, the faster is spritesheet changing), and then It sends true value, which means that Player is running, else Im not cause Space is not pressed.
And then I have to repeat this 'else' code outside of the first if statement so it sends that Player is not running if Player is not moving or his stamina Regan is false;
So I was just wondering is this kind of a bool method considered a bad practice(where you retrun true and false value in nested if, and then return false outside nested if and repeat the same code) ?
The method has a side effect, that's why it's a bad practice:
public bool isRunning()
When looking on method's signature we expect just true/false answer and nothing more. However, the method changes the instance's state:
...
if (!(move == Moving.None) && staminaRegan == true)
{
if (keyState.IsKeyDown(Keys.Space))
{
EntityAnimation.interval = 10; // <- Aaa! The interval is changed
return true;
}
...
I suggest splitting the initial method into a property and a method
// No side effect: just answer is running or not
public bool IsRunning {
get {
return (move != Moving.None) && staminaRegan && KeyState.IsKeyDown(Keys.Space);
}
}
// Put the right interval based on instance internal state
// (if it's running etc.)
public void AdjustInterval() {
if (IsRunning) // and may be other conditions
EntityAnimation.interval = 10; //TODO: move magic number into constant
else
EntityAnimation.interval = 65; //TODO: move magic number into constant
}
It is a good practice to have one return statement inside a method. Some argue about this, but it is an opinion.
it is also a good practice to make the if statement clear by removing unnecessary code:
public bool isRunning()
{
bool result = false;
if (move != Moving.None && staminaRegan)
{
if (keyState.IsKeyDown(Keys.Space))
{
EntityAnimation.interval = 10;
result = true;
}
else
{
EntityAnimation.interval = 65;
}
}
else
{
EntityAnimation.interval = 65;
}
return result;
}
You can rewrite the code as follows; then the code isn't repeated:
public bool isRunning()
{
if (move != Moving.None && staminaRegan && keyState.IsKeyDown(Keys.Space))
{
EntityAnimation.interval = 10;
return true;
}
else
{
EntityAnimation.interval = 65;
return false;
}
}
Or if you don't want the redundant else:
public bool isRunning()
{
if (move != Moving.None && staminaRegan && keyState.IsKeyDown(Keys.Space))
{
EntityAnimation.interval = 10;
return true;
}
EntityAnimation.interval = 65;
return false;
}
I would consider introducing a named boolean to self-document somewhat, and I'd rename staminaRegan to staminaIsRegenerating
public bool isRunning()
{
bool isMovingQuickly = (move != Moving.None) && staminaIsRegenerating && keyState.IsKeyDown(Keys.Space);
if (isMovingQuickly)
EntityAnimation.interval = 10;
else
EntityAnimation.interval = 65;
return isMovingQuickly;
}
Most importantly, though, you should rename the method to more accurately describe what it's doing:
public bool CheckIfRunningAndSetAnimationInterval()
I think we write code for people(other developers), of course machine execute a code but 80% of developer's work is reading the code.
Based on that I think flow of reading must be exactly same as flow of executing code - that's why I think multiply return statement not a bad thing, even better then only one return statement on the bottom of your method.
I like this style and i use it too. First, you can read the code more easily and second it has a debugging advantage as you can set breakpoints for the individual else cases. Otherwise you would need to use breakpoint conditions.

.NET IComparer sort error

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.

How i can extract year from datetime.now?

I want user to input the year of vehicle made but not more than this year. for example today is 2015, i don't want them to input 2020. but 2016 is ok.
here is my code.
property = validationContext.ObjectType.GetProperty("VehicleYear");
string vehicleYear = Convert.ToString(property.GetValue(validationContext.ObjectInstance, null));
if (!string.IsNullOrWhiteSpace(vehicleYear) && vehicleYear.Length == 4 && Convert.ToInt16(vehicleYear) >= 1980)
{
isVehicleOlderThan1981 = true;
}
else
{
isVehicleOlderThan1981 = false;
else if (value != null && Convert.ToDateTime(value) < DateTime.Now)
{
return new ValidationResult(this.ErrorMessage);
}
i only want to get a year from the DatetTime.now
Sorry i am new to the programming.
To get the year component of any date (including DateTime.Now), use this:
DateTime.Now.Year
I tried to clean your code for a bit and make it more logical (Also attached the answer you are looking for):
property = validationContext.ObjectType.GetProperty("VehicleYear");
var value = property.GetValue(validationContext.ObjectInstance, null);
int inputNumber;
//First check if input is number
if (!int.TryParse(value, out inputNumber))
{
this.ErrorMessage = "Input is not an integer!"
//you could also throw an exception here (depends on your error handling)
return new ValidationResult(this.ErrorMessage);
}
//retrieves the number of digits
int countDigits = Math.Floor(Math.Log10(year) + 1);
if (countDigits != 4)
{
this.ErrorMessage = String.Format("Input has {0} digits!",countDigits);
return new ValidationResult(this.ErrorMessage);
}
if (inputNumber > (DateTime.Now.Year + 1))
{
this.ErrorMessage = "Year is in the future!";
return new ValidationResult(this.ErrorMessage);
}
//inputNumber is now a valid year!
if(inputNumber > 1980)
{
isVehicleOlderThan1981 = true;
} else {
isVehicleOlderThan1981 = false;
}
Try this:
DateTime.Now.Year
You may also want to look at TryParse methods, it will simplify your code.
ie
int i;
if(int.TryParse("VehicleYear", out i)
{
//successful conversion, use int i for your comparisons etc.
}
else
{
//wasn't a valid year (can't be converted)
}
You need to use Year Year property from DateTime. Your else if may look like:
else if (value != null && Convert.ToDateTime(value).Year < DateTime.Now.Year)
NOTE: Convert.ToDateTime(value).Year will scream at you if value does not have correct date.
else if (value != null && Convert.ToDateTime(value) > DateTime.Now.AddYears(10))
{
//validation error
}

C# can't skip the next IF

When "thursdaytime" = Closed <- i want it to skip the next IF
Can't get the "goto" to work.
Sometimes "thursdaytime" is 07:30-16:30 <-- and then the next IF is okay
if (today == DayOfWeek.Thursday && thursdaytime == "Closed")
{
sqlreadclosed();
goto Ended;
}
if (today == DayOfWeek.Thursday && DateTime.Now.TimeOfDay > System.TimeSpan.Parse(thursdaytime.Substring(6, 5)) || DateTime.Now.TimeOfDay < System.TimeSpan.Parse(thursdaytime.Substring(0, 5)))
{
sqlreadclosed();
}
else
{
sqlreadopen();
}
Ended:
Can anyone help me out here.. im a Beginner at C# :)
If its helps, the point is, goto sqlreadclosed() if the clock is under 07:30 or over 16:30 and it works great. but when thursdaytime is "Closed" the under and over IF is crashing.. thats why i want to skip it ,if thursdaytime = Closed
Guess you wanted something like this:
if (today == DayOfWeek.Thursday && thursdaytime == "Closed")
{
sqlreadclosed();
//goto Ended;
}
else if (
today == DayOfWeek.Thursday
&& (
DateTime.Now.TimeOfDay > System.TimeSpan.Parse(thursdaytime.Substring(6, 5))
|| DateTime.Now.TimeOfDay < System.TimeSpan.Parse(thursdaytime.Substring(0, 5))
)
)
{
sqlreadclosed();
}
else
{
sqlreadopen();
}
//Ended:
Generally, using goto in C# is only necessary in very rare cases an it is considered bad practice as it leads to "spaghetti code" (i.e. code that has no clear flow). Therefore, use else if for your second condition.
Furthermore, I believe that you have a problem with your second condition, as the two time checks are mutually exclusive. Therefore, the condition will always evaluate to false. This can be fixed by using parantheses in the correct places. Generally be careful when mixing && and || in the same statement.
Alternatively, you can reformat your code to reduce the complexity and avoid the two calls to sqlreadclosed() (cudos to PJ_pavel):
if (today == DayOfWeek.Thursday)
{
if (
thursdaytime == "Closed"
&& (
DateTime.Now.TimeOfDay > System.TimeSpan.Parse(thursdaytime.Substring(6, 5))
|| DateTime.Now.TimeOfDay < System.TimeSpan.Parse(thursdaytime.Substring(0, 5))
)
)
{
sqlreadclosed();
}
else
{
sqlreadopen();
}
//Ended:
For your specific case:
if (today == DayOfWeek.Thursday) {
if(thursdaytime == "Closed" || (DateTime.Now.TimeOfDay > System.TimeSpan.Parse(thursdaytime.Substring(6, 5)) || DateTime.Now.TimeOfDay < System.TimeSpan.Parse(thursdaytime.Substring(0, 5)))){
sqlreadclosed();
else{
sqlreadopen();
}
}
But as guys said in comments: try to read more about conditional statements usage and don't use GOTO - it's a bad practice.
Try using one boolean variable like 'skipflag'. And avoid using goto statements it is considered bad practice. Goto makes the code complex and harder to read.
if (today == DayOfWeek.Thursday && thursdaytime == "Closed")
{
sqlreadclosed();
skipflag = false;
}
else{
skipflag = true;
}
if(skipflag)
{
if (today == DayOfWeek.Thursday && DateTime.Now.TimeOfDay > System.TimeSpan.Parse(thursdaytime.Substring(6, 5)) || DateTime.Now.TimeOfDay < System.TimeSpan.Parse(thursdaytime.Substring(0, 5)))
{
sqlreadclosed();
}
Else
{
sqlreadopen();
}
}
If it's "thursdaytime == "Closed"" but "today!=DayOfWeek.Thursday" then it skips the first if and goes to the second one which will raise an exception.So your first if should be either:
if (today == DayOfWeek.Thursday || thursdaytime == "Closed")
or:
if (thursdaytime == "Closed")

Categories