I am working on an application where I have implemented a functionality. The flow is like we are getting settings that can be 1 HOURS, 2 HOURS, 1 WEEK, 2 WEEKS or 1 MONTH. For Example, If I get Settings from Database like 1 HOUR then it will calculate the difference between two dates and check for 1 HOUR with the difference I am getting from the calculation. I have done it by using multiple IF checks but I want to optimize the code. Here is my implementation.
public enum TaskSchedulingEnum : int
{
[Description("1 HOUR")]
OneHour = 1,
[Description("2 HOURS")]
TwoHours = 2,
[Description("8 HOURS")]
EightHours = 8,
[Description("1 DAY")]
OneDay = 1,
[Description("3 DAYS")]
ThreeDays = 3,
[Description("1 WEEK")]
OneWeek = 1,
[Description("2 WEEKS")]
TwoWeeks = 2,
[Description("1 MONTH")]
OneMonth = 1,
[Description("ALWAYS")]
Always = 14
}
private async Task<bool> GetUserSettings(string companyId, DateTime? TaskStartDateTime)
{
// get company settings ...
var settings = await projulSettingsService.GetCompanyNotificationSettings(companyId, ProjulSettingsTypeEnum.Notifications);
var scheduleSettings = string.Empty;
if (settings.Where(x => x.Name == ProjulPortalSettings.GeneralSettingsNotificationIsDisabled).Select(x => x.Value).FirstOrDefault() == "false")
{
scheduleSettings = JsonConvert.DeserializeObject<string>(settings.Where(x => x.Name == ProjulPortalSettings.NotificationTaskAssignedImmediateWindow).Select(x => x.Value).FirstOrDefault());
if (!string.IsNullOrEmpty(scheduleSettings))
{
var _timespan = (DateTime.UtcNow - TaskStartDateTime).Value;
var difference = _timespan.TotalHours; // in hours..
TimeSpan days = TimeSpan.FromHours(difference); // days..
var weeks = (days.TotalDays % 365) / 7; // weeks
var months = (days.TotalDays % 365) / 30; // months
var list = ApplicationExtensions.GetEnumList<TaskSchedulingEnum>().ToList();
var _val = list.Where(x => x.Text == scheduleSettings).FirstOrDefault();
if (scheduleSettings == TaskSchedulingEnum.OneHour.GetEnumDescrip()
|| scheduleSettings == TaskSchedulingEnum.TwoHours.GetEnumDescrip()
|| scheduleSettings == TaskSchedulingEnum.EightHours.GetEnumDescrip())
{
if (difference == Convert.ToDouble(_val.Value))
return true;
}
else if (scheduleSettings == TaskSchedulingEnum.OneDay.GetEnumDescrip()
|| scheduleSettings == TaskSchedulingEnum.ThreeDays.GetEnumDescrip())
{
if (days.TotalDays == Convert.ToDouble(_val.Value))
return true;
}
else if (scheduleSettings == TaskSchedulingEnum.OneWeek.GetEnumDescrip()
|| scheduleSettings == TaskSchedulingEnum.TwoWeeks.GetEnumDescrip())
{
if (weeks == Convert.ToDouble(_val.Value))
return true;
}
else if (scheduleSettings == TaskSchedulingEnum.OneMonth.GetEnumDescrip())
{
if (months == Convert.ToDouble(_val.Value))
return true;
}
else if (scheduleSettings == TaskSchedulingEnum.Always.GetEnumDescrip())
{
return true;
}
return false;
}
}
return false;
}
Is there any way to write optimized and less code to achieve the functionality? I am getting the same settings from the database as mentioned in the Enum description. That can be a single value every time. Always check will return true always.
I would not recommend using enum values in this way. Let each enum value represent an arbitrary tag, that you separately convert to a duration with a extension method. For example:
public static TimeSpan ToDuration(this TaskSchedulingEnum self)
{
return self switch
{
TaskSchedulingEnum.OneHour => TimeSpan.FromHours(1),
TaskSchedulingEnum.TwoHours => TimeSpan.FromHours(2),
TaskSchedulingEnum.OneDay => TimeSpan.FromDays(1),
TaskSchedulingEnum.Always => Timeout.InfiniteTimeSpan,
};
}
Once you have a timespan to describe the duration it should be trivial to add this to any start time to get the end time or other similar operations.
Note that you might need special handling for the Always-value, since arithmetic operations with infinities will not work correctly. You might want to describe it with TimeSpan.MaxValue or use a TimeSpan? type instead, depending on how it is used.
Related
I need to limit the results of a query to CosmosDB to 1000 records, I am trying to set the feed iterator to exit once the result list hits 1000 but right now it is stopping after 100 records. Here is my current code:
public async Task<IEnumerable<AS2InboundLogRecord>> RetrieveInboundLogs(string partitionKeyName, DateTime startDate, DateTime endDate)
{
var inboundLogs = new List<AS2InboundLogRecord>();
string continuationToken = null;
int itemLimit = 1000;
QueryRequestOptions requestOptions = new QueryRequestOptions()
{
MaxItemCount = 100
};
using (FeedIterator<AS2InboundLogRecord> setIterator = dbContainer.GetItemLinqQueryable<AS2InboundLogRecord>(true, continuationToken, requestOptions)
.Where(x => (x.PartitionKey == partitionKeyName || partitionKeyName == null) &&
(x.MessageDate >= startDate) &&
(x.MessageDate <= endDate))
.ToFeedIterator())
{
while (setIterator.HasMoreResults)
{
FeedResponse<AS2InboundLogRecord> response = await setIterator.ReadNextAsync();
inboundLogs.AddRange(response);
if (response.Count >= itemLimit) { break; }
}
Console.WriteLine(inboundLogs.Count());
return inboundLogs.OrderByDescending(x => x.MessageDate);
};
}
Any input would be appricated, thanks
I think you have to correct two things:
First: you set limitCount to 100 sound like it just fetch only 100 records , if this limit fetch count set it to 1000 otherwise goto Second phrase.
Second: your if condition may not work because you compare response.Count while inboundLogs.Count should be compare.
Correction:
public async Task<IEnumerable<AS2InboundLogRecord>> RetrieveInboundLogs(string partitionKeyName, DateTime startDate, DateTime endDate)
{
var inboundLogs = new List<AS2InboundLogRecord>();
string continuationToken = null;
int itemLimit = 1000;
QueryRequestOptions requestOptions = new QueryRequestOptions()
{
MaxItemCount = 10000
};
using (FeedIterator<AS2InboundLogRecord> setIterator = dbContainer.GetItemLinqQueryable<AS2InboundLogRecord>(true, continuationToken, requestOptions)
.Where(x => (x.PartitionKey == partitionKeyName || partitionKeyName == null) &&
(x.MessageDate >= startDate) &&
(x.MessageDate <= endDate))
.ToFeedIterator())
{
while (setIterator.HasMoreResults)
{
FeedResponse<AS2InboundLogRecord> response = await setIterator.ReadNextAsync();
inboundLogs.AddRange(response);
if (inboundLogs.Count >= itemLimit) { break; }
}
Console.WriteLine(inboundLogs.Count());
return inboundLogs.OrderByDescending(x => x.MessageDate);
};
}
As cosmosdb doesn't support Skip and Take you can also use offset and limit described here
or you can also use ContinuationToken of response to make sure there is still item, something like:
do
{
response = await setIterator.ReadNextAsync();
//List<dynamic> documents = response.Resource.ToList();
continuationToken = response.ContinuationToken;
inboundLogs.AddRange(response);
} while (continuationToken != null && inboundLogs.Count <= itemLimit);
I have this while loop to get next working day excluding holidays and sundays.
But it calculates by adding 1 day. And i want that number of day to be given by the user. I get that input from the below TextBox (TboxIzin).
How can execute that while loop to do the calculation for given number of times?
int i = 1;
int sayi;
IzinIslem i1 = new IzinIslem();
int.TryParse(i1.TboxIzin.Text, out sayi);
public static DateTime GetNextWeekDay(DateTime date,
IList<Holiday> holidays, IList<DayOfWeek> weekendDays)
{
int i = 1;
int sayi;
IzinIslem i1 = new IzinIslem();
int.TryParse(i1.TboxIzin.Text, out sayi);
// always start with tomorrow, and truncate time to be safe
date = date.Date.AddDays(1);
// calculate holidays for both this year and next year
var holidayDates = holidays.Select(x => x.GetDate(date.Year))
.Union(holidays.Select(x => x.GetDate(date.Year + 1)))
.Where(x => x != null)
.Select(x => x.Value)
.OrderBy(x => x).ToArray();
// increment to get working day
while (true)
{
if (weekendDays.Contains(date.DayOfWeek) ||
holidayDates.Contains(date))
date = date.AddDays(1);
else
return date;
}
}
I get not all code paths return a value
error when i try nesting while loops.
while is a conditional loop. Here you put a non-condition in the clause and immediately follow up with a condition. Put the condition in the while clause:
while(weekendDays.Contains(date.DayOfWeek) || holidayDates.Contains(date)) {
date = date.AddDays(1);
}
return date;
The actual reason you're getting the error is that the compiler cannot predict if your if condition will ever resolve to false. If it doesn't, then your function never returns.
With the modified while loop, that may still happen, but it will result in an infinite loop, and the compiler is fine if you shoot yourself in the foot that way.
You can change your else clause to break out of the loop. And then return out of the loop.
while (true)
{
if (weekendDays.Contains(date.DayOfWeek) ||
holidayDates.Contains(date))
date = date.AddDays(1);
else
break;
}
return date;
Let's get rif of all UI in the GetNextWeekDay (like int.TryParse(i1.TboxIzin.Text, out sayi);):
public static DateTime GetNextWeekDay(DateTime date,
IEnumerable<Holiday> holidays,
IEnumerable<DayOfWeek> weekendDays) {
// public method arguments validation
if (null == holidays)
throw new ArgumentNullException(nameof(holidays));
else if (null == weekendDays)
throw new ArgumentNullException(nameof(weekendDays));
HashSet<DayOfWeek> wends = new HashSet<DayOfWeek>(weekendDays);
// Current Year and Next years - .AddYear(1)
HashSet<DateTime> hds = new HashSet<DateTime>(holidays
.Select(item => item.Date)
.Concate(holidays.Select(item => item.Date.AddYear(1))));
for (var day = date.Date.AddDays(1); ; day = day.AddDays(1))
if (!wends.Contains(day.DayOfWeek) && ! hds.Contains(day))
return day;
}
Or if you prefer Linq, the loop can be rewritten as
return Enumerable
.Range(1, 1000)
.Select(day => date.Date.AddDays(day))
.TakeWhile(item => item.Year - date.Year <= 1)
.First(item => !wends.Contains(item.DayOfWeek) && !hds.Contains(item));
How would I get the number of weekday hours between two dates? (There's a lot of business days calculations, but doesn't seem to be much on weekday hours - not business/opening hours, just hours that aren't weekends).
This is my stab at it - is there a better way?
void Main()
{
// Works
DateTime start = new DateTime(2013,6,15,0,0,0); // Saturday
DateTime end = new DateTime(2013,6,17,10,0,0); // Monday
// Result = 10 (OK)
GetBusinessHours(start, end).Dump();
// Bugs
start = new DateTime(2013,6,14,0,0,0); // Friday
end = new DateTime(2013,6,15,0,0,0); // Saturday
// Result = 0 (Bug) - should be 24
GetBusinessHours(start, end).Dump();
}
public double GetBusinessHours(DateTime start, DateTime end)
{
double result = (end - start).TotalHours;
int weekendDays = Enumerable.Range(0, 1 + end.Subtract(start).Days).Select(offset => start.AddDays(offset)).Count(d => d.DayOfWeek == DayOfWeek.Sunday || d.DayOfWeek == DayOfWeek.Saturday);
double weekendDeltaHours = 0;
if (start.DayOfWeek == DayOfWeek.Saturday ||
start.DayOfWeek == DayOfWeek.Sunday)
{
weekendDays--;
weekendDeltaHours = start.Date.AddDays(1).Subtract(start).TotalHours;
}
else if (end.DayOfWeek == DayOfWeek.Saturday ||
end.DayOfWeek == DayOfWeek.Sunday)
{
weekendDeltaHours = (end - end.Date).TotalHours;
}
result = result - (weekendDays * 24) - weekendDeltaHours;
return result;
}
(Props to Ani for Enumerable.Range trick).
Not that this will be the most efficient method, it will work for what you require.
var end = DateTime.Now;
var start = end.AddDays(-100);
var weekend = new[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
var count = Enumerable.Range(0, Convert.ToInt32(end.Subtract(start).TotalHours))
.Count(offset => !weekend.Contains(start.AddHours(offset).DayOfWeek));
It might be worth putting a check that the number of hours isn't too big for an int. However this would mean a date range of > 245,000 years!
I have this following code which will return all the current semesters. How do I identify if there is "Summer" semester in the collection and replace it with "Summer I" dynamically?
return activeSemester.Select(c => new ActiveSemester
{
id = c.SemesterId,
name = c.Name, // Here I want to check if it is Summer
}).ToList();
Update:
Summer semesters hold 3 Summer names but the dates are different. I just want to name it in order. Also each one has unique SemesterId.
return activeSemester.Select(c => new ActiveSemester
{
id = c.SemesterId,
name = c.Name == "Summer" ? "Summer I" : c.Name
}).ToList();
So you can accurately account for your 3 different summer sessions, I think a conditional approach would make more sense. If you implement any kind of incrementing method and pass data to this method that is not sorted correctly, you could tag an ActiveSemster as "Summer I" when the dates or semester id match "Summer II"
You could either include the condition directly in a LINQ query or create a method that will identify the summer based on date or semster id
return activeSemester.Select(c => new ActiveSemester
{
id = c.SemesterId,
name = c.Name == "Summer" ? GetSummmer(c.StartDate, c.EndDate) : c.Name
}).ToList();
private string GetSummer(DateTime startDate, DateTime endDate)
{
if (startDate == summer1Start || endDate == summer1End)
return "Summer I";
if (startDate == summer2Start || endDate == summer2End)
return "Summer II";
if (startDate == summer3Start || endDate == summer3End)
return "Summer III";
return "Unknown Summer";
}
private string GetSummer(Integer semesterId)
{
if (semesterId == summer1Id)
return "Summer I";
if (semesterId == summer2Id)
return "Summer II";
if (semesterId == summer3Id)
return "Summer III";
return "Unknown Summer";
}
return activeSemester.Select(c => new ActiveSemester
{
id = c.SemesterId,
name = c.Name.Replace("Summer", "Summer I") // Here I want to check if it is Summer
}).ToList();
While I'm at it: why are you calling .ToList()? That's almost always wrong, and it's generally better to go for IEnumerable<T> rather than IList<T> or List<T>.
For a generalized solution for assignment of any semester number for any term you could do the following by sorting on semester start date. This way you don't have to rely on equaliting on dates or ids. Not as elegant or performing perhaps as psubsee2003 answer. ConvertToRomanNumeral can be implemented by searching SO.
public IEnumerable<ActiveSemester> GetActiveSemesters()
{
int summerSemesterNumber = 1;
int winterSemesterNumber = 1;
foreach (ActiveSemester activeSemester in _activeSemesters.OrderBy(c => c.StartDate))
{
if (activeSemester.Name == "Summer")
{
yield return new ActiveSemester(activeSemester)
{
Name = string.Format("{0} {1}", activeSemester.Name,
ConvertToRomanNumeral(summerSemesterNumber++))
};
}
else if (activeSemester.Name == "Winter")
{
yield return new ActiveSemester(activeSemester)
{
Name = string.Format("{0} {1}", activeSemester.Name,
ConvertToRomanNumeral(winterSemesterNumber++))
};
}
else
{
yield return new ActiveSemester(activeSemester);
}
}
}
I'm looking for a LINQ query that will select only those objects whose date interval is not higher than 20 seconds. For example:
AuthenticationEssay[] essays = new AuthenticationEssay[] {
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(20), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(24), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(29), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(38), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(125), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(347), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(400), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(422), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(446), Success = false },
new AuthenticationEssay() { Date = DateTime.Now.AddSeconds(467), Success = false }
};
I want to select only the first occurence of those objects whose date interval is not longer than 20 seconds against the next object. In this case, the query should return only the first 4 objects. Any idea? :(
UPDATE
Sorry, I forgot to mention that I'm sorting the array by descending order. So yes, the position in the array shouldn't have any effect on the query.
What about this?
var query from i in Enumerable.Range(1, count - 1)
let current = list[i]
let previous = list[i - 1]
// I see some empty positions in your example, nullability check
where current != null && previous != null
where (current.Date - previous.Date).TotalSeconds < 20
select previous;
EDIT: Obviously you have to call First() in order to get only the first element of the sequence.
query.First();
EDIT 2: I have just read you are ordering your results descending. In this case the query will be slightly different:
var query from i in Enumerable.Range(1, count - 1)
let current = list[i]
let previous = list[i - 1]
// I see some empty positions in your example, nullability check
where current != null && previous != null
where (previous.Date - current.Date).TotalSeconds < 20
select current;
It ain't pretty, but here you go...
var result = Enumerable.Range(0, essays.Count() - 1)
.Select(i => new {Essays1 = essays[i], Essays2 = essays[i + 1]})
.Where(a => a.Essays2 != null)
.Where(a => a.Essays2.Date - a.Essays1.Date < new TimeSpan(0, 0, 0, 20))
.Select(a => a.Essays1);
Does it have to be LINQ? I love LINQ, but I think something like this would be more readable...
var result = new List<AuthenticationEssay>();
for (var i = 0; i < (essays.Count() - 1); i++)
{
if (essays[i + 1] != null)
if (essays[i + 1].Date - essays[i].Date < new TimeSpan(0, 0, 0, 20))
result.Add(essays[i]);
}
It is probably possible to do it using built-in Linq operators, but in this case I think writing a specific method is easier. You could do something like that:
static IEnumerable<AuthenticationEssay> Filter(IEnumerable<AuthenticationEssay> list)
{
AuthenticationEssay last = null;
AuthenticationEssay previous = null;
foreach(var item in list)
{
if (last == null)
{
// Always return the first item
yield return item;
}
else if ((item.Date - last.Date).TotalSeconds >= 20)
{
yield return item;
}
previous = last;
last = item;
}
if (previous != null && last != null && (last.Date - previous.Date).TotalSeconds <= 20)
yield return last;
}
Of course it would be possible to make it more reusable, by making the method generic and passing a predicate as a parameter, but since it's a very specific requirement, I'm not sure it would be very useful...