Enumerate Class Properties in C# - c#

I have a class Similar to this
public class Model
{
public TimeSpan Time1 {get; set;}
public TimeSpan Time2 { get; set; }
public TimeSpan Time3 { get; set; }
public TimeSpan Time4 { get; set; }
}
Now Let's Imagine I have to populate the times during runtime and then Figure out the time remaining between Time 1 and Time 2, then when that passes Find the time remaining between Time2 and Time3 and so on. However, I need to take into account what the time is right now.
For Example:
Now it is 1:00 PM
Time1=5:00 AM
Time 2 = 12:00 PM
Time 3= 4:00 PM
Time 4 = 6:00 PM
So since the time is 1:00PM, I need to find the difference between Time 2 and Time 3
Now is there a smarter way other than reflection to determine this? Should i add something in my class

If you need to keep the existing structure of your class, you could add a method to enumerate through the times:
public class Model
{
public TimeSpan Time1 {get; set;}
public TimeSpan Time2 { get; set; }
public TimeSpan Time3 { get; set; }
public TimeSpan Time4 { get; set; }
public IEnumerable<TimeSpan> GetTimes()
{
yield return Time1;
yield return Time2;
yield return Time3;
yield return Time4;
}
}
And use it like this:
foreach (TimeSpan time in model.GetTimes())
{
// use the TimeSpan
}

Why would you not use an array or list?
public class Model
{
public List<DateTime> Dates { get; set; }
}

If I'm understanding your question correctly, why not have a Dictionary<TimeSpan, string> where the key is actual TimeSpan, and the value is the name, e.g. "Time1"? This way, you can sort the keys, find the pair that you need, and then get at their names.

You could add a property or method that would return the times as a list for easier processing. Then your external code could access the times as either a list or as individual properties. You still have to explicitly access each property, but at least it's from within the same class, so you're not coupling your external code to the data structure as tightly.

This is untested and needs some optimization, but just as a thought example, use an array and iterate through it.
public class Model
{
private TimeSpan[] _timeSpans = new TimeSpan[4] { new TimeSpan(), new TimeSpan(), new TimeSpan(), new TimeSpan() };
public TimeSpan Time1
{
get { return _timeSpans[0]; }
set { _timeSpans[0] = value; }
}
public TimeSpan Time2
{
get { return _timeSpans[1]; }
set { _timeSpans[1] = value; }
}
public TimeSpan Time3
{
get { return _timeSpans[2]; }
set { _timeSpans[2] = value; }
}
public TimeSpan Time4
{
get { return _timeSpans[3]; }
set { _timeSpans[3] = value; }
}
// DateTime.TimeOfDay holds the time portion of a time
public TimeSpan GetDifference(TimeSpan currentTime)
{
int start = -1;
for(int i = 0; i<_timeSpans.Length;i++)
{
if(_timeSpans[i] >= currentTime)
{
start = i;
break;
}
}
if(start == -1) throw new ArgumentException("handle the case where currentTime is smaller than all of them");
int end = (start + 1 < _timeSpans.Length) ? start + 1 : 0;
return _timeSpans[end] - _timeSpans[start];
}

Constructing an array provides for a simple linq statement to calculate the time span:
public class Model
{
public TimeSpan Time1 { get; set; }
public TimeSpan Time2 { get; set; }
public TimeSpan Time3 { get; set; }
public TimeSpan Time4 { get; set; }
public TimeSpan GetSpan(TimeSpan time)
{
var times = new[] { Time1, Time2, Time3, Time4 };
return Enumerable.Range(1, times.Length - 1)
.Select(i => new[] { times[i - 1], times[i] })
.Where(t => t[0] <= time && time < t[1])
.Select(t => t[1] - t[0])
.FirstOrDefault();
}
}

Related

How to pass time value from page.cs to ViewModel

How to paas time value from page.cs to ViewModel.
timevalue is string like "14h:15m"
Here I'm trying...
Page.cs
_model.StartTime = int.Parse(timevalue);
ViewModel
public DateTime StartTime { get; set; } = DateTime.Today;
public DateTime EndTime { get; set; }
public int Duration { get; set; }
private TimeSpan[] _StartTimeValues { get; set; }
private int _StartTimeIndex = -1;
public int StartTime
{
get { return _StartTime; }
set
{
_StartTimeIndex = value;
if (value >= 0)
{
StartTime =StartTime.Date.Add(_StartTimeValues[value]);
EndTime = StartTime.AddMinutes(Duration);
}
OnPropertyChanged(nameof(StartTime));
}
}
At first, please change the _StartTimeIndex's type to SpanTime, because the TimeDate.Add need a parameter type of SpanTime. Such as:
string time = "12h45m";
char[] hourandminute = new char[] { 'h', 'm' };
string[] temp = time.Split(hourandminute);
int hour = int.Parse(temp[0]);
int minutes = int.Parse(temp[1]);
TimeSpan timeSpan = new TimeSpan(hour, minutes, 0);

Extracting information out of a dictionary...An alternative to if/else statements

I have a dictionary that looks like this:
Dictionary<string, DateTime> Seasons = new Dictionary<string, DateTime>
{
{ "WINTER_START", Date1},
{ "WINTER_END", Date2 },
{ "SUMMER_START", Date3 },
{ "SUMMER_END", Date4 }
};
Let's say I have today's date var today = DateTime.Today.Date and I want to know if today's date falls in winter or summer. I've done this using an If/Else statement, but something inside me tells me there is a better way..
string currentSeason = "";
if (today >= Seasons["WINTER_START"] && today <= Seasons["WINTER_END"])
{
currentSeason = "WINTER";
}
else if (today >= Seasons["SUMMER_START"] && today <= Seasons["SUMMER_END"])
{
currentSeason = "SUMMER";
}
Not sure if you are locked down to using a Dictionary for this, but i think this is a friendlier version and how i would've tackled the problem:
void Main()
{
var seasons = new List<Season>
{
new Season("Winter", Date1, Date2),
new Season("Summer", Date3, Date4)
};
var today = DateTime.Today;
// null if no matching season was found
string currentSeason = seasons.FirstOrDefault(season => season.InSeason(today))?.Name;
}
public class Season
{
public Season(string name, DateTime start, DateTime end)
{
Name = name;
Start = start;
End = end;
}
public string Name { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public bool InSeason(DateTime input)
{
return input >= Start && input <= End;
}
}
Also a minor remark: Current_Season is not a good variable name for a local variable. This might help you improve your naming.

Split string into struct

Is there a way assign split parts into struct?
I have strings that show times:
10:25, 10:55
11:05, 11:50
12:20, 13:10
I am iterating theses lines from text file and want to assign to ParkTime struct
public struct ParkTime
{
public string startTime { get; set; }
public string endTime { get; set; }
}
I tried to use LINQ but, first I need to validate lines using regular expressions.
I suggest to use DateTime in your struct (or better a class but this is not important here). In this way the validation of the input is more focused to the expected value and you could avoid using regex expressions
List<ParkTime> parktimes = new List<ParkTime>();
foreach (string line in File.ReadLines("file.txt"))
{
bool isValid = true;
string[] times = line.Split(',');
DateTime dtInit;
if(!DateTime.TryParseExact(times[0].Trim(), "HH:mm",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.NoCurrentDateDefault,
out dtInit))
isValid = false;
DateTime dtEnd;
if (!DateTime.TryParseExact(times[1].Trim(), "HH:mm",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.NoCurrentDateDefault,
out dtEnd))
isValid = false;
if (isValid)
parktimes.Add(new ParkTime() { startTime = dtInit, endTime = dtEnd });
}
public struct ParkTime
{
public DateTime startTime { get; set; }
public DateTime endTime { get; set; }
}
And about using class or struct to model your data, this is a classical answer from E.Lippert (main developer for the C# Language)
You can use Linq Where to only select valid lines and then Select to create structs out of the lines.
lines
.Where(line => validate(line))
.Select(line => new ParkTime() {startTime = getStartTime(line), endTime = getEndTime(line));
Here are the methods used and their signature:
public bool validate(string line){
return true; //validate if the line is correct
}
public string getStartTime(string line){
return null; //select the start time from the line
}
public string getEndTime(string line){
return null; //select the end time from the line
}
For task like this, I use a simple TimeSlot struct which holds a timespan rather than an end time. Some operations are easier or more direct but, of course, that will depend on your planned usage for the parking hours.
public struct TimeSlot
{
private DateTime _start;
private TimeSpan _span;
public DateTime Start
{
get
{
if (_start == null)
{
_start = DateTime.Today;
}
return _start;
}
set
{
_start = value;
}
}
public TimeSpan Span
{
get
{
if (_span == null)
{
_span = new TimeSpan(0);
}
return _span;
}
set
{
if (value.Ticks >= 0)
{
_span = value;
}
}
}
public DateTime End
{
get
{
return Start.Add(Span);
}
}
public TimeSlot(DateTime start, TimeSpan span)
{
_start = start;
_span = span.Ticks >= 0 ? span : new TimeSpan(0);
}
}
If you know how ti iterate your lines you just need to split each line and then assing trimmed values to properties.
ParkTime pt = new ParkTime();
string[] arr = "10:25, 10:55".Split(",");
pt.startTime = arr[0].Trim();
pt.endTime = arr[1].Trim();

How to make an if statement to show xml attribute based on current time

I have a XML like this:
<PrayerTime
Day ="1"
Month="5"
Fajr="07:00"
Sunrise="09:00"
Zuhr="14:00"
/>
A class like this:
public class PrayerTime
{
public string Fajr { get; set; }
public string Sunrise { get; set; }
public string Zuhr { get; set; }
}
And something to get the value like this:
XDocument loadedCustomData = XDocument.Load("WimPrayerTime.xml");
var filteredData = from c in loadedCustomData.Descendants("PrayerTime")
where c.Attribute("Day").Value == myDay.Day.ToString()
&& c.Attribute("Moth").Value == myDay.Month.ToString()
select new PrayerTime()
{
Fajr = c.Attribute("Fajr").Value,
Sunrise = c.Attribute("Sunrise").Value,
};
myTextBox.Text = filteredData.First().Fajr;
How can i based by current time of day say that if time is between the value of Fajr and the Value of Sunrise, then myTextBox should show the value of Fajr.
If value of current time is between sunrise and Zuhr, show Zuhr?
How can i get it to show the attribute name in myTextBox2?
For example, myTextBox shows value "07:00", and myTextBox2 shows "Fajr"?
First modify the class as per #abatischcev
public class PrayerTime
{
public TimeSpan Fajr { get; set; }
public TimeSpan Sunrise { get; set; }
public TimeSpan Zuhr { get; set; }
}
Then modify the linq query select part as:
select new PrayerTime()
{
Fajr = TimeSpan.Parse(c.Attribute("Fajr").Value),
Sunrise = TimeSpan.Parse(c.Attribute("Sunrise").Value),
Zuhr = TimeSpan.Parse(c.Attribute("Zuhr").Value)
};
then your check should be:
var obj = filteredData.First();
TimeSpan currentTime = myDay.TimeOfDay;
string result = String.Empty;
if (currentTime >= obj.Fajr && currentTime < obj.Sunrise)
{
result = "Fajar";
}
else if (currentTime >= obj.Sunrise && currentTime < obj.Zuhr)
{
result = "Zuhar";
}
textbox1.Text = result;
(By the way, Zuhr time should be between Zuhr and Asar :))
First, keep not string but TimeSpan object:
public TimeSpan Fajr { get; set; }
public TimeSpan Sunrise { get; set; }
To do this parse XML into DateTime:
TimeSpan ts = TimeSpan.Parse(c.Attribute("attr"));
So:
TimeSpan now = DateTime.Now.TimeOfDay; // time part only
var data = filteredData.First();
string result = null;
if (data.Fajr <= now && now < data.Sunrise); // notice operators greed
result = data.Fajr;
else if (data.Sunrise <= now && now <= data.Zuhr)
result = data.Zuhr;
myTextBox.Text = result;
The problem here is your code is "stringly typed". I would be better to use type that is design for time e.g. DateTime, but to quick fix it:
// don't you need a third one here?
select new PrayerTime()
{
Fajr = c.Attribute("Fajr").Value,
Sunrise = c.Attribute("Sunrise").Value,
};
Tu get current hour:
int currentHour = DateTime.Now.Hour;
Then is just simple comparison of two integers.
var data = filteredData.First();
int fajrHour = int.Parse(data.Fajr.Substring(0, 2), CultureInfo.InvariantCulture);
int sunriseHour = int.Parse(data.Sunrise.Substring(0, 2), CultureInfo.InvariantCulture);
int zuhrHour = int.Parse(data.Zuhr.Substring(0, 2), CultureInfo.InvariantCulture);
if(fajrHour <= currenHour && currenHour < sunriseHour)
{
myTextBox.Text = data.Fajr; // or to show value fajrHour.ToString()
}
if(sunriseHour <= currenHour && currenHour < zuhrHour)
{
myTextBox.Text = data.Zuhr; // zuhrHour.ToString()
}
// don't you need a third one here?

Generating a value dependent on another value with AutoPoco

I'm using the great .NET library AutoPoco for creating test and Seed Data.
In my model I have 2 date properties, StartDate and EndDate.
I want the EndDate to be 3 hours after the start Date.
I've created a custom Data source for autopoco below that returns a random Datetime between a min and max date
class DefaultRandomDateSource : DatasourceBase<DateTime>
{
private DateTime _MaxDate { get; set; }
private DateTime _MinDate { get; set; }
private Random _random { get; set; }
public DefaultRandomDateSource(DateTime MaxDate, DateTime MinDate)
{
_MaxDate = MaxDate;
_MinDate = MinDate;
}
public override DateTime Next(IGenerationSession session)
{
var tspan = _MaxDate - _MinDate;
var rndSpan = new TimeSpan(0, _random.Next(0, (int) tspan.TotalMinutes), 0);
return _MinDate + rndSpan;
}
}
But in AutoPoco's configuration how can i get my EndDate to be say, 3 hours after the autogenerated start Date?
Here's the autopoco config
IGenerationSessionFactory factory = AutoPocoContainer.Configure(x =>
{
x.Conventions(c => { c.UseDefaultConventions(); });
x.AddFromAssemblyContainingType<Meeting>();
x.Include<Meeting>()
.Setup((c => c.CreatedBy)).Use<FirstNameSource>()
.Setup(c => c.StartDate).Use<DefaultRandomDateSource>(DateTime.Parse("21/05/2011"), DateTime.Parse("21/05/2012"));
});
If I am correctly understanding the problem you need: to set EndDate from StartDate. I had to create a new DataSource and get current item which we are constructing and read value from it. I haven't thoroughly checked but it might fail if StartDate is set after EndDate (though I think the properties are set in the order they are setup, read source code for AutoPoco). Also I am using latest version from CodePlex as of today (20 Feb 2012).
public class MeetingsGenerator
{
public static IList<Meeting> CreateMeeting(int count)
{
var factory = AutoPocoContainer.Configure(x =>
{
x.Conventions(c => { c.UseDefaultConventions(); });
x.Include<Meeting>()
.Setup((c => c.CreatedBy)).Use<FirstNameSource>()
.Setup(c => c.StartDate).Use<DefaultRandomDateSource>
(DateTime.Parse("21/May/2012"),
DateTime.Parse("21/May/2011"))
.Setup(c => c.EndDate).Use<MeetingEndDateSource>(0, 8);
});
return factory.CreateSession().List<Meeting>(count).Get();
}
}
public class Meeting
{
public string CreatedBy { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
public class MeetingEndDateSource : DatasourceBase<DateTime>
{
private int mMin;
private int mMax;
private Random mRandom = new Random(1337);
public MeetingEndDateSource(int min, int max)
{
mMin = min;
mMax = max;
}
public override DateTime Next(IGenerationContext context)
{
var node = (TypeGenerationContextNode)((context.Node).Parent);
var item = node.Target) as Meeting;
if (item == null)
return DateTime.Now;
return item.StartDate.AddHours(mRandom.Next(mMin, mMax + 1));
}
}
class DefaultRandomDateSource : DatasourceBase<DateTime>
{
private DateTime _MaxDate { get; set; }
private DateTime _MinDate { get; set; }
private Random _random = new Random(1337);
public DefaultRandomDateSource(DateTime MaxDate, DateTime MinDate)
{
_MaxDate = MaxDate;
_MinDate = MinDate;
}
public override DateTime Next(IGenerationContext context)
{
var tspan = _MaxDate - _MinDate;
var rndSpan = new TimeSpan(0
, _random.Next(0, (int)tspan.TotalMinutes)
, 0);
return _MinDate + rndSpan;
}
}

Categories