Reputation: 2427
I'm working on the following problem:
I want to fill a two-dimensional [365,2] Array. The first value is supposed to hold the date: starting with January 1st and ending with December the 31st. The second value is supposed to hold the corresponding Zodiac Sign to each date:
e.g. array[0, 0]
holds 101 and array[0, 1]
holds Aries and so on.
I've written a function:
public static void fill_array(string[,] year_zodiac, int days, string zodiac, string startdate, int starting_day)
{
//function to fill array with date and zodiac
int startdate_int = 0;
for (int i = starting_day; i <= days; i++)
{
year_zodiac[i, 0] = startdate;
year_zodiac[i, 1] = zodiac;
startdate_int = Int32.Parse(startdate);
startdate_int++;
startdate = startdate_int.ToString();
}
and call it like this:
fill_array(main_array, 18, "Aries", "101", 0);
This has to be done for every Zodiac sign. I circumvented the month break problem by simply calling fill_array
twice (i.e. I call it once for the part of Aries in December and once for the part of aries in January).
This Solution works, but it seems incredibly crude to me.
Can anybody give me some pointers towards a more elegant solution?
Upvotes: 4
Views: 2601
Reputation: 1569
Why do you specifically want an array? Surely a more sophisticated data structure would help to encapsulate the functionality and help you to isolate your remaining code from knowing about the workings of it, and would also let you take account in the future of more subtle variations in the exact dates where they vary by year if you ever want to do that.
For example:
public class SO23182879
{
public enum StarSign
{
Aquarius, Pisces, Aries, Taurus, Gemini, Cancer,
Leo, Virgo, Libra, Scorpio, Sagittarius, Capricorn
};
static DateTime[] starSignStartDates = new DateTime[]
{
new DateTime(DateTime.Now.Year, 1, 20),
new DateTime(DateTime.Now.Year, 2, 19),
new DateTime(DateTime.Now.Year, 3, 21),
new DateTime(DateTime.Now.Year, 4, 20),
new DateTime(DateTime.Now.Year, 5, 21),
new DateTime(DateTime.Now.Year, 6, 21),
new DateTime(DateTime.Now.Year, 7, 23),
new DateTime(DateTime.Now.Year, 8, 23),
new DateTime(DateTime.Now.Year, 9, 23),
new DateTime(DateTime.Now.Year, 10, 23),
new DateTime(DateTime.Now.Year, 11, 22),
new DateTime(DateTime.Now.Year, 12, 22),
new DateTime(DateTime.Now.Year, 1, 20),
};
private class StarSignDateRange
{
public StarSign Sign { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
private List<StarSignDateRange> signStartDates = new List<StarSignDateRange>();
public SO23182879()
{
int date = 0;
foreach (StarSign sign in Enum.GetValues(typeof(StarSign)))
{
signStartDates.Add(new StarSignDateRange
{
Sign = sign,
StartDate = starSignStartDates[date],
EndDate = starSignStartDates[date + 1]
});
++date;
}
}
public StarSign Sign(DateTime date)
{
return signStartDates.First(
sd => date.Month == sd.StartDate.Month && date.Day >= sd.StartDate.Day ||
date.Month == sd.EndDate.Month && date.Day < sd.EndDate.Day
).Sign;
}
public void Test()
{
IList<DateTime> testDates = new List<DateTime>
{
new DateTime(2014,1,1),
new DateTime(2014,1,19),
new DateTime(2014,1,20),
new DateTime(2014,4,19),
new DateTime(2014,4,20),
new DateTime(2014,12,21),
new DateTime(2014,12,22),
new DateTime(2014,12,31),
};
foreach (DateTime d in testDates)
Console.WriteLine(string.Format("{0} is in {1}", d, Sign(d)));
}
}
As you'll see, I have completed the code which I had started earlier, and added a Test()
method for you to see that the edge conditions work. I am grateful to Hogan for pointing out the "year zero" problem and other similar "gotchas" in my earlier sketch.
Upvotes: 8
Reputation: 70538
Here is a class that does what you want, it has been tested. I did not fill out all signs on the first example, but when I re-factored it I did. I suggest you test this well as I only tested a few cases and might have missed some edge cases.
As has been pointed out to me by @ClickRick, I did start with his array/enum design, but found that lacking when using Linq and moved to a List. I also had to fix his data array so it would compile. I'm giving credit as is due.
public class Signs
{
static List<string> SignList = new List<string>() { "Aquarius", "Pisces", "Aries", "Taurus", "Not Found"};
static DateTime[] startDates = {
new DateTime(DateTime.Now.Year,1,21),
new DateTime(DateTime.Now.Year,2,20),
new DateTime(DateTime.Now.Year,3,21),
new DateTime(DateTime.Now.Year,4,21),
new DateTime(DateTime.Now.Year,12,31) };
static public string Sign(DateTime inDate)
{
return SignList.Zip(startDates, (s, d) => new { sign = s, date = d })
.Where(x => (inDate.Month*100)+inDate.Day <= (x.date.Month*100)+x.date.Day)
.Select(x => x.sign)
.First();
}
}
re-factored (this is clearer with the example above first)
public class Signs
{
static List<string> SignList = new List<string>() {
"Capricorn", "Aquarius", "Pisces", "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Not Found" };
static List<int> startDates = new List<int>() {
// month * 100 + day of month
120, 219, 320, 420, 521, 621, 722, 821, 923, 1023, 1122, 1222, 1232, 9999 // edge marker
};
static public string Sign(DateTime inDate)
{
return SignList[startDates.TakeWhile(d => (inDate.Month*100)+inDate.Day > d).Count()];
}
}
Upvotes: 11