I have this challange that I finished which asked to print out a string according to the provided schedule. Here is an example:
var restaurant = new Restaurant(
new OpeningHour(8,16), // Sunday
new OpeningHour(8,17), // Monday
new OpeningHour(8,17), // Tuesday
new OpeningHour(8,17), // Wednesday
new OpeningHour(8,16), // Thursday
new OpeningHour(8,16), // Friday
new OpeningHour(8,16) // Saturday
);
expected output result = "Sun, Thu - Sat: 8-16, Mon - Wed: 8-17"
What I did was essentially:
Create a List of Days, OpenHours, and CloseHours
Create a HashSet of the days so that I can compare the days
Create a for loop according to HashSet and Days
Seperate the Start, Middle, and Ending
Concatenate the result according to the open and close hours as well as the gap between days
I have tried my best but I know for a fact that my code is not efficient at all, instead messy. I am trying to improve my C# skills please help. Here is my messy code:
namespace Livit
{
using System;
using System.Collections.Generic;
using System.Linq;
public class Restaurant
{
public WeekCollection<OpeningHour> OpeningHours { get; private set; }
public Restaurant() {
// No opening hours available for restaurant
}
public Restaurant(OpeningHour monday, OpeningHour tuesday, OpeningHour wednesday, OpeningHour thursday, OpeningHour friday, OpeningHour saturday, OpeningHour sunday)
{
OpeningHours = new WeekCollection<OpeningHour>(monday, tuesday, wednesday, thursday, friday, saturday, sunday);
}
// THE EMPHASIS OF THE CHALLANGE IS THIS FUNCTION RIGHT HERE!!!
// Parse the date into desired format
public string DateParser(List<DayOfWeek> days, List<TimeSpan> openHours, List<TimeSpan> closeHours)
{
HashSet<string> availableRanges = new HashSet<string>();
List<string> timeRanges = new List<string>();
DayOfWeek current = DayOfWeek.Sunday;
string result = "";
for (int i = 0 ; i < days.Count; i++){
string timeRange = openHours[i].ToString().Substring(1,1)+'-'+closeHours[i].ToString().Substring(0,2);
availableRanges.Add(timeRange);
timeRanges.Add(timeRange);
}
List<string> arToList= availableRanges.ToList();
for (int i = 0 ; i < arToList.Count; i++)
{
for (int j = 0 ; j < timeRanges.Count; j++){
if(timeRanges[j] == arToList[i]){
// First Item
if(j==0 ){
result += days[j].ToString().Substring(0,3);
}
// Last Item
else if(j==timeRanges.Count-1){
char last = result.Last();
if(last != ' '){
result += " - ";
}
result += days[j].ToString().Substring(0,3);
}
// Everything in the middle
else{
if(days[j]-current > 1){
result += ", ";
}
if(timeRanges[j] != timeRanges[j-1] ){
result += days[j].ToString().Substring(0,3);
} else if (timeRanges[j] == timeRanges[j-1]){
char last = result.Last();
if(last != ' '){
result += " - ";
}
if(timeRanges[j] != timeRanges[j+1]){
result += days[j].ToString().Substring(0,3);
}
}
}
current = days[j];
}
}
result += ": " + arToList[i];
if(i!=arToList.Count-1){
result += ", ";
}
}
Console.WriteLine(result);
return result;
}
public string GetOpeningHours()
{
// Declare List for each attribute
List<DayOfWeek> days = new List<DayOfWeek>();
List<TimeSpan> openHours = new List<TimeSpan>();
List<TimeSpan> closeHours = new List<TimeSpan>();
// Call the opening and closing hours from each day and feed into new array
foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)).OfType<DayOfWeek>().ToList()) {
TimeSpan openHour = OpeningHours.Get(day).OpeningTime;
TimeSpan closeHour = OpeningHours.Get(day).ClosingTime;
days.Add(day);
openHours.Add(openHour);
closeHours.Add(closeHour);
}
return DateParser(days,openHours,closeHours);
throw new NotImplementedException();
}
}
public class OpeningHour
{
public TimeSpan OpeningTime { get; private set; }
public TimeSpan ClosingTime { get; private set; }
public OpeningHour(TimeSpan openingTime, TimeSpan closingTime)
{
OpeningTime = openingTime;
ClosingTime = closingTime;
}
public OpeningHour(int openingHour, int closingHour)
{
OpeningTime = TimeSpan.FromHours(openingHour);
ClosingTime = TimeSpan.FromHours(closingHour);
}
}
public class WeekCollection<T>
{
private Dictionary<DayOfWeek, T> _collection;
public WeekCollection(T sunday, T monday, T tuesday, T wednesday, T thursday, T friday, T saturday)
{
_collection = new Dictionary<DayOfWeek, T>();
_collection.Add(DayOfWeek.Sunday, sunday);
_collection.Add(DayOfWeek.Monday, monday);
_collection.Add(DayOfWeek.Tuesday, tuesday);
_collection.Add(DayOfWeek.Wednesday, wednesday);
_collection.Add(DayOfWeek.Thursday, thursday);
_collection.Add(DayOfWeek.Friday, friday);
_collection.Add(DayOfWeek.Saturday, saturday);
}
public T Get(DayOfWeek dayOfWeek)
{
return _collection[dayOfWeek];
}
}
}
Currently, I am still trying to find a better way in doing this challange. Any help would be appriciated.
P.S. I highlighted the part where my concatenation is occuring, this part is basically the emphasis of the whole challange
Related
Been stuck on this task for a while now any help would be greatly appreciated.
So I have user input of Patient ID, Staff ID, visitType and DateTime from the Presentation Layer which I want to add to a list through the Business Layer.
The dateTime is inputted as a string and I can store It as a string fine but what I am trying to do is convert it into DateTime and then be able to store it in a list. this is where I encounter errors.
here is my code in presentation layer(MainWindow.cs), where I am pulling the information to be stored;
private void BtnAddVisit_Click(object sender, RoutedEventArgs e)
{
txtOutput.Text = "";
try
{
if (healthSystem.addVisit(new int[2] { 1, 3 }, 1, visitTypes.assessment, "01/01/2020 09:00")) //Should be OK
txtOutput.Text += "Visit 1 added.\n";
}
catch (Exception ex)
{
txtOutput.Text += ex.Message;
}
txtOutput.Text += healthSystem.getVisitList();
}
and here is my code in the business layer(HealthFacade);
public Boolean addVisit(int[] staff, int patient, int type, string dateTime)
{
//if the number of objects in the visit list is equal to 6, clear the list to avoid repeated output in textbox
if (visit.Count == 6)
{
visit.Clear();
}
//converting from string to dateTime
for (int i = 0; i < visit.Count; i++)
{
//the original task was to store 6 instances so thats why is says < 7
if (visit.Count < 7)
{
DateTime oDate = Convert.ToDateTime(dateTime);
}
}
//adding all the visits, regardless of if the data is valid or not
Visit v = new Visit();
v.Staff = staff;
v.Patient = patient;
v.Type = type;
v.DateTime = oDate;
//adds instance of visit to the visit list
visit.Add(v);
return true;
My understanding would be that I would then write v.DateTime = oDate; but it tells me 'oDate' does not exist in the current context.
the code for my Visit class is here;
using System;
using System.Collections.Generic;
using System.Text;
namespace BusinessLayer
{
class Visit
{
int[] staff;
int patient, type;
string dateTime;
public Visit()
{
}
// Constructor for Staff, using example from week 5 practical
public Visit(int [] aStaff, int aPatient, int aType, string aDateTime)
{
staff = aStaff;
patient = aPatient;
type = aType;
dateTime = aDateTime;
}
public int[] Staff
{
set { staff = value; }
get { return staff; }
}
public int Patient
{
set { patient = value; }
get { return patient; }
}
public int Type
{
set { type = value; }
get { return type; }
}
public string DateTime
{
set { dateTime = value; }
get { return dateTime; }
}
}
}
The reason I am trying to do this is so that I can set up a doctors appointments system and make sure that no 2 appointments are at the same time and therefore clash.
Thanks in advance for any help you can give!
The problem here is that the variable oDate only exists in the scope where you declared it.
In your case it's only usable inside your if statement.
Your function should look like this for you to access the variable when needed:
public Boolean addVisit(int[] staff, int patient, int type, string dateTime)
{
//if the number of objects in the visit list is equal to 6, clear the list to avoid repeated output in textbox
if (visit.Count == 6)
{
visit.Clear();
}
DateTime oDate = Convert.ToDateTime(dateTime);;
//converting from string to dateTime
for (int i = 0; i < visit.Count; i++)
{
//the original task was to store 6 instances so thats why is says < 7
if (visit.Count < 7)
{
//DateTime oDate = Convert.ToDateTime(dateTime);
// Because you definded oDate inside your if clause it is only accessible inside the clause
}
}
//adding all the visits, regardless of if the data is valid or not
Visit v = new Visit();
v.Staff = staff;
v.Patient = patient;
v.Type = type;
v.DateTime = oDate;
//adds instance of visit to the visit list
visit.Add(v);
return true;
Convert.ToDateTime may not work with different local settings. Because a DateTime is not allways dd/MM/yyyy HH:mm.
You can try like this if you are sure that your datetime string is allways formatted like this "01/01/2020 09:00".
var dateTime = "01/01/2020 09:00";//Assume your date is that.
DateTime dtInvariant = DateTime.ParseExact(dateTime, "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture);
//If you want to work with strings,
var datePart = dateTime.Split(' ')[0];
var timePart = dateTime.Split(' ')[1];
var day = int.Parse(datePart.Split('/')[0]);
var month = int.Parse(datePart.Split('/')[1]);
var year = int.Parse(datePart.Split('/')[2]);
var hour = int.Parse(timePart.Split(':')[0]);
var minute = int.Parse(timePart.Split(':')[1]);
var myDate = new DateTime(year, month, day, hour,minute, 0);
I have these 3 strings (using # as a delimiter):
Name#startTime#endTime#room
Meeting#19:00:00#20:30:00#Conference
Hist 2368#19:00:00#20:30:00#Large Conference Room
Hist 2368#09:00:00#10:30:00#Large Conference Room
I want to know how would I generate this
Conference 9:00:00 19:00:00
Large Conference Room 10:30:00 20:30:00
Large Conference Room 20:30:00 22:00:00
So what this is generating are the times of a room that are free. In the top 3 strings we can see that Conference is occupied from 19:00:00 to 20:30:00 so the free time is 9:00:00 to 19:00:00 (A day starts at 9:00:00 and ends at 22:00:00).
So to make this task relatively easy you need to define a class that understands how to split a period of time given a potentially overlapping period of time.
Here's that class:
private sealed class Period : IEquatable<Period>
{
public DateTime StartTime { get; private set; }
public DateTime EndTime { get; private set; }
public Period(DateTime startTime, DateTime endTime)
{
this.StartTime = startTime;
this.EndTime = endTime;
}
public override bool Equals(object obj)
{
if (obj is Period)
return Equals((Period)obj);
return false;
}
public bool Equals(Period obj)
{
if (obj == null)
return false;
if (!EqualityComparer<DateTime>.Default.Equals(
this.StartTime, obj.StartTime))
return false;
if (!EqualityComparer<DateTime>.Default.Equals(
this.EndTime, obj.EndTime))
return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<DateTime>.Default
.GetHashCode(this.StartTime);
hash ^= EqualityComparer<DateTime>.Default
.GetHashCode(this.EndTime);
return hash;
}
public override string ToString()
{
return String.Format("{{ StartTime = {0}, EndTime = {1} }}",
this.StartTime, this.EndTime);
}
public IEnumerable<Period> Split(Period period)
{
if (period.StartTime <= this.StartTime)
{
if (period.EndTime <= this.StartTime)
yield return this;
else if (period.EndTime >= this.EndTime)
yield break;
else
yield return new Period(period.EndTime, this.EndTime);
}
else if (period.StartTime < this.EndTime)
{
yield return new Period(this.StartTime, period.StartTime);
if (period.EndTime < this.EndTime)
{
yield return new Period(period.EndTime, this.EndTime);
}
}
else
yield return this;
}
}
The important code here is the IEnumerable<Period> Split(Period period) method. It goes through each possible case when comparing two time periods and returns zero, one or two periods that can be left after the split.
So, given your input data is like this:
var lines = new []
{
"Meeting#19:00:00#20:30:00#Conference",
"Hist 2368#19:00:00#20:30:00#Large Conference Room",
"Hist 2368#09:00:00#10:30:00#Large Conference Room",
};
var full_day =
new Period(
DateTime.Parse("09:00"),
DateTime.Parse("22:00"));
I can then run this code to determine the free times:
var free_times =
from line in lines
let parts = line.Split('#')
let Start = DateTime.Parse(parts[1])
let End = DateTime.Parse(parts[2])
orderby Start, End
group new Period(Start, End) by parts[3] into groups
select new
{
Room = groups.Key,
FreePeriods =
groups.Aggregate(new [] { full_day },
(ys, x) => ys.SelectMany(y => y.Split(x)).ToArray()),
};
The result I get is:
NB: Your example results in the question do not match your data. I have assumed that your data is correct and ignored your example results.
I have the following datetimes:
Start = 15/12/2012 13:00:00
End = 16/02/2013 14:00:00
How can I split that in 3 parts for each month?
- 15-12-2012 13:00:00 -> 01-01-2013 00:00:00
- 01-01-2013 00:00:00 -> 01-02-2013 00:00:00
- 01-02-2013 00:00:00 -> 16-02-2013 14:00:00
The total timespan must remain the same.
Can this easily be done with LINQ?
sure, try this (with little helper class included)
Process:
var Start = DateTime.Parse("15 Dec 2012 13:00:00");
var End = DateTime.Parse("16 Feb 2013 14:00:00");
var runningDate = Start;
while (runningDate < End)
{
var nextMonthSeed = runningDate.AddMonths(1);
var to = DateHelper.Min(new DateTime(nextMonthSeed.Year, nextMonthSeed.Month, 1), End);
Console.WriteLine("{0} -> {1}", runningDate.ToString("dd-MM-yyyy HH:mm:ss"), to.ToString("dd-MM-yyyy HH:mm:ss"));
runningDate = to;
}
Helper class:
public static class DateHelper
{
public static DateTime Min(DateTime date1, DateTime date2)
{
return (date1 < date2 ? date1 : date2);
}
}
You could try something like these extension methods:
public static class SomeExtensions {
public static IEnumerable<Tuple<DateTime, DateTime>> GetIntervals(
this DateTime from,
DateTime to) {
var currentFrom = from;
var currentTo = from.AdvanceToStartOfNextMonth();
while (currentTo < to) {
yield return Tuple.Create(currentFrom, currentTo);
currentFrom = currentTo;
currentTo = currentFrom.AdvanceToStartOfNextMonth();
}
yield return Tuple.Create(currentFrom, to);
}
public static DateTime AdvanceToStartOfNextMonth(this DateTime #this) {
var newMonth = #this.Month + 1;
var newYear = #this.Year;
if (newMonth == 13) {
newMonth = 1;
newYear++;
}
return new DateTime(newYear, newMonth, 1);
}
}
and then use them like so:
public class Etc {
public static void Foo() {
DateTime start = ...
DateTime stop = ....
Tuple<DateTime, DateTime>[] intervals = start.GetIntervals(stop).ToArray();
// or simply
foreach (var interval in start.GetIntervals(stop))
Console.WriteLine(interval);
}
}
EDIT
And here's a little test I just tried out (and it looks alright, I think):
class Program {
static void Main(string[] args) {
DateTime start = DateTime.Now.Subtract(TimeSpan.FromDays(170));
DateTime stop = DateTime.Now;
foreach (var interval in start.GetIntervals(stop))
Console.WriteLine(interval);
Console.ReadKey(intercept: true);
}
}
and that produced these results (in a console app):
END OF EDIT
I want to extract all the sundays in the current month and have this code:
private string GetDatesOfSundays(DateTime DatMonth)
{
string sReturn = "";
int iDayOffset = DatMonth.Day - 1;
DatMonth = DatMonth.AddDays(System.Convert.ToDouble(-DatMonth.Day + 1));
DateTime DatMonth2 = DatMonth.AddMonths(1).AddDays(System.Convert.ToDouble(-1));
while (DatMonth < DatMonth2)
{
if (DatMonth.DayOfWeek == System.DayOfWeek.Sunday)
{
if (sReturn.Length > 0) sReturn += ",";
sReturn += DatMonth.ToShortDateString();
}
DatMonth = DatMonth.AddDays(1.0);
}
return sReturn;
}
[HttpGet]
public ActionResult TradeUKKPISearchesData()
{
string allSundaysInMonth = GetDatesOfSundays(System.DateTime.Now);
//var reportData = _reportingService.GetTradeUKKPISearches();
//return View(reportData);
}
the problem lies with my type string for allSundaysInMonth and is also empty ofcourse. The sReturn is of type string but then again I pass a date(I know :) ) but what type should allSundaysInMonth be? sReturn does have the correct dates in...I need to display these dates in a dropdown in the view of the controller so the user can select any of the sundays for which he/she needs to run a report for.
thanks
How about
private IEnumerable<DateTime> GetDatesOfSundays(DateTime DatMonth)
{
int iDayOffset = DatMonth.Day - 1;
DatMonth = DatMonth.AddDays(System.Convert.ToDouble(-DatMonth.Day + 1));
DateTime DatMonth2 =
DatMonth.AddMonths(1).AddDays(System.Convert.ToDouble(-1));
while (DatMonth < DatMonth2)
{
if (DatMonth.DayOfWeek == System.DayOfWeek.Sunday)
{
yield return DatMonth;
}
DatMonth = DatMonth.AddDays(1.0);
}
}
I would be tempted to rewrite your function as an extension somthing like this
public static IEnumerable<Datetime> DaysOfMonth(
this DateTime any,
DayOfWeek day)
{
// start at first of month
var candidate = new DateTime(any.Year, any.Month, 1);
var offset = (int)day - (int)candidate.DayOfWeek;
if (offset < 0)
{
offset += 7
}
candidate = candidate.AddDays(offset);
while (cadidate.Month == any.Month)
{
yield return candidate;
candidate = candidate.AddDays(7.0)
}
}
Then you could use it like this
var allSundaysInMonth = DateTime.Now.DaysOfMonth(DayOfWeek.Sunday);
If you want to convert an IEnumerable<DateTime> to a string you could do this,
var listOfDates = string.Join<DateTime>(", ", allSundaysInMonth);
using this string.Join overload
If you really want the days as a DateTime[] you could do this (but there is no need)
DateTime[] allSundaysInMonth = GetDatesOfSundays(DateTime.Now).ToArray();
or for my extension example
var allSundaysInMonth = DateTime.Now.DaysOfMonth(DayOfWeek.Sunday).ToArray();
You can go for DateTime[] or IEnumerable<DateTime>.
Your method signature should be
private IEnumerable<DateTime> GetDatesOfSundays(DateTime DatMonth)
or
private DateTime[] GetDatesOfSundays(DateTime DatMonth)
If you havn't worked with IEnumerable go for this
private DateTime[] GetDatesOfSundays(DateTime DatMonth)
{
List<DateTime> lst = new List<DateTime>();
DatMonth = DatMonth.AddDays(-DatMonth.Day + 1);
DateTime DatMonth2 = DatMonth.AddMonths(1).AddDays(System.Convert.ToDouble(-1));
while (DatMonth < DatMonth2)
{
if (DatMonth.DayOfWeek == System.DayOfWeek.Sunday)
{
lst.Add(DatMonth);
DatMonth = DatMonth.AddDays(7);
continue;
}
DatMonth = DatMonth.AddDays(1);
}
return lst.ToArray();
}
and call it as
DateTime[] allSundaysInMonth = GetDatesOfSundays(System.DateTime.Now);
I'm trying to make a calendar using wpf. By using itemsPanel and more, I have a grid with 7 columns(sunday-saturday) and 6 rows(week# of month). If i can find the starting position of the first of each month by getting the weekday and week number(of the month), how can I find the week number(0-5 of each month)? Also can't I somehow just fill in the calendar from there? I'm lost and I don't know what else to try.
public partial class SchedulePage : Page
{
MainWindow _parentForm;
public int dayofweek;
public SchedulePage(MainWindow parentForm)
{
InitializeComponent();
_parentForm = parentForm;
// DateTime date = new DateTime(year, month, day);
_parentForm.bindings = new BindingCamper();
_parentForm.bindings.schedule.Add(new Schedule { WeekNo = (int) getWeekNumber(), WeekDay = dayofweek });
DataContext = _parentForm.bindings;
// lblTest.Content = dates(2011, 10, 27);
}
public double getWeekNumber()
{
dayofweek = getWeekDay(2011, 10, 31);
double h = dayofweek / 7;
double g = Math.Floor(h);
return g;
}
public int getWeekDay(int year, int month, int day)
{
//year = 2011;
//month = 10;
//day = 27;
int[] t = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
// year -= month < 3;
return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7;
}
You must use Calendar.GetDayOfWeek and Calendar.GetWeekOfYear in preference to writing yourself.
You can guarantee that if you write any date / time handling code yourself it will contain faults and won't work in different locales.
public class Row
{
public string MonthWeek { get; set; }
public string Year { get; set; }
public string Month { get; set; }
public string Day { get; set; }
public string WeekOfYear { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var l = new List<Row>();
DateTime startDate = DateTime.Now;
DateTime d = new DateTime(startDate.Year, startDate.Month, 1);
var cal = System.Globalization.DateTimeFormatInfo.CurrentInfo.Calendar;
var ms = cal.GetWeekOfYear(new DateTime(d.Year, d.Month, 1), System.Globalization.CalendarWeekRule.FirstDay, System.DayOfWeek.Sunday);
for (var i = 1; d.Month == startDate.Month; d = d.AddDays(1))
{
var si = new Row();
var month_week = (d.Day / 7) + 1;
si.MonthWeek = month_week.ToString();
si.Month = d.Year.ToString();
si.Year = d.Month.ToString();
si.Day = d.Day.ToString();
si.WeekOfYear = cal.GetWeekOfYear(d, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString();
l.Add(si);
}
dataGrid1.ItemsSource = l;
}
}
together with the obligatory DataGrid in the XAML:
<DataGrid AutoGenerateColumns="true" Name="dataGrid1" />
You can use Calendar.GetWeekOfYear from Globalization to do this.
Here's the MSDN docs for it: http://msdn.microsoft.com/en-us/library/system.globalization.calendar.getweekofyear.aspx
You should pass the appropriate culture properties from CultureInfo.CurrentCulture to GetWeekOfYear so that you match the current culture properly.
Example:
int GetWeekOfYear(DateTime date)
{
return Calendar.GetWeekOfYear(
date,
CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek
);
}
You could easily modify this into an extension method on DateTime:
static int GetWeekOfYear(this DateTime date)
{
return Calendar.GetWeekOfYear(
date,
CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule,
CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek
);
}
With #Polynomial answer, I have this error:
An object reference is required for the non-static field, method, or property...
If you instanciate GregorianCalendar before then you can call the method GetWeekOfYear !
private static int GetWeekNumber(DateTime time)
{
GregorianCalendar cal = new GregorianCalendar();
int week = cal.GetWeekOfYear(time, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
return week;
}