Reputation: 9396
I need to find the sun-sign
of a given person based on his age.
For eg,
Capricorn December 22 – January 20
Aquarius January 21 – February 18
Pisces February 19 – March 19
Aries March 20 – April 19
Taurus April 20 – May 20
Gemini May 21 – June 20
Cancer June 21 – July 22
Leo July 23 – August 22
Virgo August 23 – September 22
Libra September 23 – October 22
Scorpio October 23 – November 21
Sagittarius November 22 – December 21
I wrote this code,
public enum Months
{
January = 1, February, March, April, May, June, July, August, September, October, November, December,
}
var person = new Person(name:"mady", age:20, dateTime: new DateTime(2011,09,16));
if (person.DOB.Month == (int)Months.December)
{
if (person.DOB.Day >= 22)
return "Capricorn";
else
return "Sagittarius";
} ...
....
....
....
The IF
statements grow consistently and might become a nightmare if tomorrow the list grows.
Is there an elegant way of finding out the Sunsign ? Enumerable
or Range
in .NET doesn't seem to fit this case or is this the only way of writing the code ?
Upvotes: 1
Views: 244
Reputation: 17627
Create a class StarSign:
class StarSign
{
public readonly string Name;
public readonly DateTime StartDate;
public readonly DateTime EndDate;
public bool Contains(DateTime date);
}
Add all the star signs to a collection StarSigns. Then for any given DateTime date (of the person) do
foreach (var sign in StarSigns)
{
if (sign.Contains(date))
{
Console.WriteLine("I am a: " + sign.Name);
break;
}
}
Edit, responding to your comment:
The Contains function can easily compare dates, just make sure you ignore the year:
public bool Contains(DateTime date)
{
DateTime startNoYear = new DateTime(1904, StartDate.Month, StartDate.Day);
DateTime endNoYear = new DateTime(1904, EndDate.Month, EndDate.Day);
DateTime dateNoYear = new DateTime(1904, date.Month, date.Day);
return dateNoYear >= startNoYear && dateNoYear <= endNoYear;
}
So yes, if you have many many StarSigns, this will affect performance. Normaly you will only have 12, and since you know you are dealing with a closed set, you can afford to do it this way.
When it comes to optimization, you will also want to store startNoYear and endNoYear and not calculate them each time you run Contains. Calculate them in the constructor; I'm only doing it in the method so it's easier to understand. Even faster would be to work on DateTime properties directly and avoid creating new DateTime objects altogether. As far as this example goes, I opt for simplicity over optimization.
Upvotes: 6
Reputation: 439
The David Božjak's answer is a great choice.
I think the class could be abstract and implemented for every sign. Also the StartDate, EndDate and the date pass as parameter need to ignore the year. I made in this way:
public abstract class StarSign
{
public readonly string Name;
public readonly DateTime StartDate;
public readonly DateTime EndDate;
protected StarSign(string name, DateTime startDate, DateTime endDate)
{
Name = name;
StartDate = startDate;
EndDate = endDate;
}
public virtual bool Contains(DateTime date)
{
date = new DateTime(1, date.Month, date.Year);
return date >= StartDate && date <= EndDate;
}
}
public class AquariusStarSign : StarSign
{
public AquariusStarSign()
: base("Aquarius", new DateTime(1, 1, 21), new DateTime(1, 2, 18))
{
}
}
public class CapricornStarSign : StarSign
{
public CapricornStarSign()
: base("Capricorn", new DateTime(1, 12, 21), new DateTime(1, 1, 20))
{
}
public override bool Contains(DateTime date)
{
if (date.Month == StartDate.Month)
return date.Day >= StartDate.Day;
if (date.Month == EndDate.Month)
return date.Day <= EndDate.Day;
return false;
}
}
Upvotes: 0
Reputation: 130
linq is good.. just use your list... have a look at this one
public class sing
{
public string singName {
get { return _singName; }
set { _singName = value; }
}
private string _singName;
public DateTime singStart {
get { return _singStart; }
set { _singStart = value; }
}
private DateTime _singStart;
public DateTime singEnd {
get { return _singEnd; }
set { _singEnd = value; }
}
private DateTime _singEnd;
public void findSing(System.DateTime usersDate)
{
List<sing> ListOfSings = new List<sing>();
sing scorpio = new sing();
System.DateTime startD = new System.DateTime(1910, 10, 23);
System.DateTime endD = new System.DateTime(1910, 11, 21);
scorpio.singName = "scorpio";
scorpio.singStart = startD;
scorpio.singEnd = endD;
ListOfSings.Add(scorpio);
//' ....etc all the others
dynamic hismonth = usersDate.Month;
dynamic hisDay = usersDate.Day;
System.DateTime fixedDate = new System.DateTime(1910, hismonth, hisDay);
dynamic q = (from i in ListOfSings where i.singStart >= fixedDate && i.singEnd <= fixedDatei).ToList;
MessageBox.Show("your sing is: " + q.FirstOrDefault.singName);
}
}
Upvotes: 0
Reputation: 172270
Note that you can compare dates:
if (new DateTime(2012, 1, 1) < new DateTime(2012, 2, 1)) ...
Thus, I would suggest that
and then simply use date comparisons:
DateTime dob = new DateTime(1904, person.DOB.Month, person.DOB.Day);
if (dob >= new DateTime(1904, 12, 21))
return "Aquarius";
else if (dob >= new DateTime(1904, 11, 22))
return "Sagittarius";
else if (dob >= new DateTime(1904, 10, 23))
return "Scorpio";
...
else
return "Aquarius";
An obvious improvement would be to create a List<Tuple<DateTime, String>>
and iterate through that. However, since the dates are very unlikely to change in the next hundred years, hardcoding them in the if
conditions might suffice.
Upvotes: 3
Reputation: 10899
May you could build a small Dictionary of sun-sign, which stors the Name of the sun sign as key and it's timespan as value. The timespan would be the first to the last date. Then there a standard time function to tell if the persons DOB is in the timespan. Mayb you need to strip out the year of birth, but that should be easy.
As a final touch you could use linq:
var sunsigns as Dictionary<string, TimeSpan>();
// adding sun-signs here
var sunsign = (from s in sunsigns where (methodToTellIfItsinRange(s)) select s).first();
Upvotes: 0
Reputation: 4443
You can use switch statement
switch (person.DOB.Month)
{
.....
case 12:
if (day >= 22) return "Capricorn"; else return "Sagittarius";
break;
.......
}
Upvotes: 1