I'm gathering 3 user inputs which I would want to run against this array and then return the designated information with (there are 2 others but don't want to clog up the question):
//variable to hold the return value from readline - set to 0
int c1 = 0;
//if that var is 0 go - do while loop - while var = 0 go
if (c1 == 0)
{
while (c1 == 0)
{
Console.WriteLine("Please enter a C# Course ID:\n 10 = Basic C# \n 11 = Advanced C# \n 12 = Complex C# \n Only the course ID is valid as an entry");
int ID;
//Readline - pass variable back for what was entered
bool isNumeric = int.TryParse(Console.ReadLine(), out ID);
//validation check as to what was entered ; i.e. >=10 <=12
if (ID == 10)
{
Console.WriteLine("You have chosen:\n 10 = Basic C#");
c1 = 10;
}
else if (ID == 11)
{
Console.WriteLine("You have chosen:\n 11 = Advanced C#");
c1 = 11;
}
else if (ID == 12)
{
Console.WriteLine("You have chosen:\n 12 = Complex C#");
c1 = 12;
}
//if var outside of range, hard set to 0
else if (ID >= 10 && ID <= 12)
{
Console.WriteLine("Enter a valid input between 10 and 12");
}
}
The c1 variable is then ideally put in and run in a foreach loop or something similar in order to pick the correct array item.
Courses[] courses =
{
//C# Courses which can be chosen- (int courseID, int streamID, string courseName, new DateTime (YYYY/MM/DD))
new Csharp(10, 1, "Basic C#", new DateTime(2020, 7, 19)),
new Csharp(11, 2, "Advanced C#", new DateTime(2020, 8, 8)),
new Csharp(12, 3, "Complex C#", new DateTime(2020, 7, 18)),
//Java Courses that can be chosen
new Java(20, 1, "Basic Java", new DateTime(2020, 10, 7)),
new Java(21, 2, "Advanced Java", new DateTime(2020, 11, 14)),
new Java(22, 3, "Complex Java", new DateTime(2020, 9, 26)),
//Netwroking Courses that can be chosen
new NetWorking(30, 1, "Basic Networking", new DateTime(2020, 12, 29)),
new NetWorking(31, 2, "Advanced Networking", new DateTime(2021, 1, 15)),
new NetWorking(32, 3, "Complex Networking", new DateTime(2020, 12, 13))
};
The Courses class is set up like so:
public abstract class Courses
{
public DateTime CourseDate { get; set; }
public int StreamID { get; private set; }
public string CourseName { get; private set; }
public int CourseID { get; private set; }
public Courses(int courseID, int streamID, string courseName, DateTime courseDate)
{
this.CourseID = courseID;
this.StreamID = streamID;
this.CourseName = courseName;
this.CourseDate = courseDate;
}
}
The main question is how to compare c1 (user input) against the CourseID (first number in the array item). Once again, thanks in advance, sorry for the many questions!
With Linq:
var course = courses.SingleOrDefault(x => x.CourseID == c1);
if (course == null){
// No course found
}
I'm playing with "weeks". I'm retrieving data from a DB in a way like this:
My object in c# looks like this:
public class DemoObj
{
public decimal Amount { get; set; }
public int Month { get; set; }
public int Week { get; set; }
}
I'm grouping data as I wrote on the right of the image, and it works fine and on the end it looks like this:
Month 6
Week 2
Month 8
Week 2
Month 8
Week 3
But I would like to achieve next:
Check if there are no all 4 weeks in one month for example for month with value 8, lets add missing weeks even if that will be empty object, filling week value would be enought so at the end value would look like this:
Month 8
Week 1
Month 8
Week 2
Month 8
Week 3
Month 8
Week 4
So, check if there are not all 4 weeks for value 8, if not, than lets add missing ones..
Here is my current code:
var query = await _context.product
.AsNoTracking()
.Where(x => (x.PaymentDate != null && x.PaymentDate > DateTime.UtcNow.AddMonths(-4))).ToListAsync();
var groupedData = query.GroupBy(x => CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(x.PaymentDate ?? DateTime.UtcNow, CalendarWeekRule.FirstDay, DayOfWeek.Monday))
.Select(product => new productsChartDTO
{
Week = GetWeekNumberOfMonth(product.FirstOrDefault().PaymentDate.Value),
Amount = product.Sum(x => x.Amount),
Month = product.FirstOrDefault().PaymentDate.Value.Month
});
So only for returned months, if there are not all 4 weeks (Values with 1,2,3,4) lets find which one are missing and lets add them something like that.
Any kind of help would be awesome
Thanks
You could group by month and then create a productsChartDTO[4] with an entry for each week:
var groupedData = query.GroupBy(x => (x.PaymentDate ?? DateTime.UtcNow).Month)
.Select(month =>
{
var weeks = Enumerable.Range(1, 4).Select(x => new productsChartDTO() { Month = month.Key, Week = x, Amount = 0 }).ToArray();
foreach (var date in month)
{
int week = GetWeekNumberOfMonth(date.PaymentDate ?? DateTime.UtcNow);
weeks[week - 1].Amount += date.Amount;
}
return weeks;
})
.SelectMany(x => x);
There might be some cleaner way to do it but this works.
static void Main(string[] args)
{
var groupedData = new List<DemoObj>
{
new DemoObj { Amount = 11, Month = 1, Week = 1 },
new DemoObj { Amount = 133, Month = 1, Week = 2 },
new DemoObj { Amount = 323, Month = 1, Week = 3 },
// Needs to add week 4
new DemoObj { Amount = 2342, Month = 2, Week = 1 },
// Needst to add week 2
new DemoObj { Amount = 23433, Month = 2, Week = 3 }
// Needs to add etc..
};
var fullData = AddMissingValues(groupedData);
}
private static IEnumerable<DemoObj> AddMissingValues(IEnumerable<DemoObj> valuesFromDb)
{
var results = valuesFromDb.ToList();
var possibleMonths = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
var possibleWeeks = new[] { 1, 2, 3, 4 };
foreach (var possibleMonth in possibleMonths)
{
foreach (var possibleWeek in possibleWeeks)
{
if (results.Any(x => x.Month == possibleMonth && x.Week == possibleWeek) == false)
{
results.Add(new DemoObj { Month = possibleMonth, Week = possibleWeek });
}
}
}
return results.OrderBy(x => x.Month).ThenBy(x => x.Week);
}
public class DemoObj
{
public decimal Amount { get; set; }
public int Month { get; set; }
public int Week { get; set; }
}
I have a generic list of names whose number depends on what the user selects. I want to arrange those names in a range of times of the day that the user input before.
For example: we have 3 names, the user input 08:00-17:00. From 08:00-17:00 we have 9 hours -> 9/3=3, so arrange the names in a 3 hours templates.
This is how it looks in code:
List<string> myList = new List<string>();
myList.Add(Name1);
myList.Add(Name2);
myList.Add(Name3);
Console.WriteLine("Enter hours range: ");
Console.ReadLine(); //in this case the user input is 08:00-17:00
if (myList.Count == 3)
{
Console.WriteLine("8-11 " + myList.ElementAt(0)); //Name1
Console.WriteLine("11-14 " + myList.ElementAt(1)); //Name2
Console.WriteLine("14-17 " + myList.ElementAt(2)); //Name3
}
The problem comes, when there are more than 3 names and the hours are halved (lets say 17:30).
Any ideas on how to do this kind of thing?
You could do something like
private static void CalculateTimeIntervals()
{
var startTime = new DateTime(2014, 8, 15, 8, 0, 0);
var endTime = new DateTime(2014, 8, 15, 17, 0, 0);
var lengthOfTime = endTime - startTime;
var numberOfPeople = 4;
var dividedLengthOfTime = lengthOfTime.Ticks / numberOfPeople;
var people = new List<Person>();
for (int i = 1; i <= numberOfPeople; i++)
{
people.Add(
new Person
{
Id = i,
StartTime = startTime.AddTicks((dividedLengthOfTime * i) - dividedLengthOfTime),
EndTime = startTime.AddTicks(dividedLengthOfTime * i)
});
}
}
where Person looks like
public class Person
{
public int Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
Given a period starting from StartingDate to EndingDate.
I want to get the intervals within that period starting given StartingMonth and EndingMonth.
Example :
StartingMonth = april (4)
EndingMonth = november (11)
Periods :
Period A : StartingDate = (2014, 03, 01); EndingDate = (2015, 02, 28);
Period B : StartingDate = (2014, 07, 01); EndingDate = (2015, 06, 30);
Period C : StartingDate = (2014, 01, 01); EndingDate = (2015, 12, 31);
Would return :
Period A : 1 sub-period = (2014, 4, 1) - (2014, 11, 30)
Period B : 2 sub-periods = (2014, 7, 1) - (2014, 11, 30) ; (2015, 4, 1) - (2015, 6, 30)
Period C : 2 sub-periods = (2014, 4, 1) - (2014, 11, 30) ; (2015, 4, 1) - (2015, 11, 30)
I have tried this (seems to be the hard way and does not manage multiple sub-periods):
May be an easier way using LINQ ?
if (StartingDate.Month < startingMonth && EndingDate.Month < endingMonth)
{
periods.Add(new PeriodInterval
{
StartDate = new DateTime(StartingDate.Year, startingMonth, 1),
EndDate = new DateTime(StartingDate.Year, endingMonth, EndingDate.Day)
});
}
if (StartingDate.Month > startingMonth && EndingDate.Month > endingMonth)
{
periods.Add(new PeriodInterval
{
StartDate = new DateTime(StartingDate.Year, startingMonth, 1),
EndDate = new DateTime(StartingDate.Year, endingMonth, EndingDate.Day)
});
}
if (StartingDate.Month < startingMonth && EndingDate.Month > endingMonth)
{
periods.Add(new PeriodInterval
{
StartDate = new DateTime(StartingDate.Year, startingMonth, 1),
EndDate = new DateTime(StartingDate.Year, endingMonth, EndingDate.Day)
});
}
if (StartingDate.Month > startingMonth && EndingDate.Month < endingMonth)
{
periods.Add(new PeriodInterval
{
StartDate = new DateTime(StartingDate.Year, startingMonth, 1),
EndDate = new DateTime(StartingDate.Year, endingMonth, EndingDate.Day)
});
}
The idea is to returns the blue periods within the red period :
class Discount
{
public int DiscountID { get; set; } //You will need some Key field if you are storing these in a database.
public DateTime issueDate { get; set; }
public DateTime expirationDate { get; set; }
public List<PeriodInterval> intervals { get; set; }
public Discount(DateTime IssueDate, DateTime ExpirationDate)
{
issueDate = IssueDate;
expirationDate = ExpirationDate;
intervals = new List<PeriodInterval>();
}
public void AddInterval(DateTime StartDate, DateTime EndDate)
{
intervals.Add(new PeriodInterval() {
StartMonth=StartDate.Month,
StartDay=StartDate.Day,
EndMonth=EndDate.Month,
EndDay=EndDate.Day
});
}
public List<Period> GetPeriods()
{
List<Period> periods=new List<Period>();
int yearCount = expirationDate.Year-issueDate.Year+1; //+1: Run at least one year against the periods.
for (int i = 0; i < yearCount; i++)
{
//Loop through all the years and add 'Periods' from all the PeriodInterval info.
foreach (PeriodInterval pi in intervals)
{
var period = pi.GetPeriod(issueDate, expirationDate, i);
if (period != null)
periods.Add(period);
}
}
return periods;
}
}
class Period
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
class PeriodInterval
{
public int PeriodIntervalID { get; set; } //You will need some Key field if you are storing these in a database.
public int DiscountID { get; set; } //Foreign Key to Discount. This is alsof for database storage.
public int StartMonth { get; set; }
public int StartDay { get; set; }
public int EndMonth { get; set; }
public int EndDay { get; set; }
public Period GetPeriod(DateTime issueDate, DateTime expirationDate, int Year)
{
DateTime PeriodStart = new DateTime(issueDate.AddYears(Year).Year, StartMonth, StartDay);
DateTime PeriodEnd = new DateTime(issueDate.AddYears(Year).Year, EndMonth, EndDay);
PeriodStart=new DateTime(Math.Max(PeriodStart.Ticks, issueDate.Ticks)); //Limit period to the max of the two start dates.
PeriodEnd = new DateTime(Math.Min(PeriodEnd.Ticks, expirationDate.Ticks)); //Limit period to the min of the two end dates.
if(PeriodEnd>PeriodStart) //If a valid period
{
return new Period()
{
StartDate = PeriodStart,
EndDate = PeriodEnd
};
}
//Default Return Null
return null;
}
}
I built a console application to test this out:
static void Main(string[] args)
{
List<Discount> Discounts = new List<Discount>();
Discount d1 = new Discount(new DateTime(2014, 3, 1), new DateTime(2015, 02, 28));
Discount d2 = new Discount(new DateTime(2014, 7, 1), new DateTime(2015, 06, 30));
Discount d3 = new Discount(new DateTime(2014, 01, 1), new DateTime(2015, 12, 31));
Discounts.Add(d1);
Discounts.Add(d2);
Discounts.Add(d3);
foreach (Discount d in Discounts)
{
d.AddInterval(new DateTime(2014, 4, 1), new DateTime(2014, 11, 30));
Console.WriteLine("IssueDate:{0} ExpirationDate:{1}", d.issueDate, d.expirationDate);
foreach (Period p in d.GetPeriods())
{
Console.WriteLine("Start:{0} End:{1}", p.StartDate, p.EndDate);
}
}
Console.ReadLine();
}
Here's what that prints out:
You can use the Time Period Library for .NET:
// ----------------------------------------------------------------------
public void ExtractSubPeriods()
{
foreach ( ITimePeriod subPeriod in GetSubPeriods(
new TimeRange( new DateTime( 2014, 4, 1 ), new DateTime( 2015, 2, 28 ) ) ) )
{
Console.WriteLine( "SubPeriods 1: {0}", subPeriod );
foreach ( ITimePeriod subPeriod in GetSubPeriods(
new TimeRange( new DateTime( 2014, 7, 1 ), new DateTime( 2015, 6, 30 ) ) ) )
{
Console.WriteLine( "SubPeriods 2: {0}", subPeriod );
}
foreach ( ITimePeriod subPeriod in GetSubPeriods(
new TimeRange( new DateTime( 2014, 4, 1 ), new DateTime( 2015, 12, 31 ) ) ) )
{
Console.WriteLine( "SubPeriods 3: {0}", subPeriod );
}
} // ExtractSubPeriods
// ----------------------------------------------------------------------
public ITimePeriodCollection GetSubPeriods( ITimeRange timeRange )
{
ITimePeriodCollection periods = new TimePeriodCollection();
periods.Add( timeRange );
int startYear = periods.Start.Year;
int endYear = periods.End.Year + 1;
for ( int year = startYear; year <= endYear; year++ )
{
periods.Add( new TimeRange( new DateTime( year, 4, 1 ), new DateTime( year, 12, 1 ) ) );
}
TimePeriodIntersector<TimeRange> intersector = new TimePeriodIntersector<TimeRange>();
return intersector.IntersectPeriods( periods );
} // GetSubPeriods
A few things to consider:
As humans, we typically use fully-inclusive ranges for date-only values, while we use half-open intervals for time-only or date+time values. Think: 2 days from Jan 1 to Jan 2, but 1 hour from 1:00 to 2:00, or from Jan 1 Midnight to Jan 2 Midnight.
.Net's built-in DateTime type, is a date+time type. When you omit the time, it uses midnight. You cannot remove the time portion.
If you were to use DateTime with date-at-midnight ranges, the best you could do is choose to ignore the time portion. That makes for some tricky code, as you would have to normalize your inputs to midnight before comparing against the range. I don't recommend this approach, as it is error prone. The edge cases will pile up quickly.
Therefore, I recommend either switching to half-open intervals with DateTime, or if you need to continue to use fully-inclusive ranges then consider using the LocalDate type from Noda Time. I will show you examples of both.
Because you are accepting month numbers as inputs, consider that you should also handle the case of them being out of sequence. That is, a two-month sub-period may range from December of one year, to January of the next.
Unless there are guarantees that the outer period will fall exactly at the start and end points of a whole month, you will need to trim the results. For example, if your Period ran from Jan 3 2014 to March 9 2016, then the subperiod in 2015 would have the whole months, but 2014 would be trimmed at the start and 2016 would be trimmed at the end.
Here is how you can achieve this using DateTime and half-open date-at-midnight intervals:
public class DateTimeInterval
{
/// <summary>
/// The date and time that the interval starts.
/// The interval includes this exact value.
/// </summary>
public DateTime StartDate { get; private set; }
/// <summary>
/// The date and time that the interval is over.
/// The interval excludes this exact value.
/// </summary>
public DateTime EndDate { get; private set; }
public DateTimeInterval(DateTime startDate, DateTime endDate)
{
StartDate = startDate;
EndDate = endDate;
}
public IEnumerable<DateTimeInterval> GetSubIntervals(int startingMonth,
int endingMonth)
{
// Determine the possible ranges based on the year of this interval
// and the months provided
var ranges = Enumerable.Range(StartDate.Year,
EndDate.Year - StartDate.Year + 1)
.Select(year => new DateTimeInterval(
new DateTime(year, startingMonth, 1),
new DateTime(
startingMonth > endingMonth ? year + 1 : year,
endingMonth, 1)
.AddMonths(1)));
// Get the ranges that are overlapping with this interval
var results = ranges.Where(p => p.StartDate < this.EndDate &&
p.EndDate > this.StartDate)
.ToArray();
// Trim the edges to constrain the results to this interval
if (results.Length > 0)
{
if (results[0].StartDate < this.StartDate)
{
results[0] = new DateTimeInterval(
this.StartDate,
results[0].EndDate);
}
if (results[results.Length - 1].EndDate > this.EndDate)
{
results[results.Length - 1] = new DateTimeInterval(
results[results.Length - 1].StartDate,
this.EndDate);
}
}
return results;
}
}
Using the above code:
var interval = new DateTimeInterval(new DateTime(2014, 3, 1), // inclusive
new DateTime(2015, 3, 1)); // exclusive
var subIntervals = interval.GetSubIntervals(4, 11);
And here is how you can achieve the same thing using NodaTime.LocalDate and fully-inclusive date-only intervals:
using NodaTime;
public class LocalDateInterval
{
/// <summary>
/// The date that the interval starts.
/// The interval includes this exact value.
/// </summary>
public LocalDate StartDate { get; private set; }
/// <summary>
/// The date that the interval ends.
/// The interval includes this exact value.
/// </summary>
public LocalDate EndDate { get; private set; }
public LocalDateInterval(LocalDate startDate, LocalDate endDate)
{
StartDate = startDate;
EndDate = endDate;
}
public IEnumerable<LocalDateInterval> GetSubIntervals(int startingMonth,
int endingMonth)
{
// Determine the possible ranges based on the year of this interval
// and the months provided
var ranges = Enumerable.Range(StartDate.Year,
EndDate.Year - StartDate.Year + 1)
.Select(year => new LocalDateInterval(
new LocalDate(year, startingMonth, 1),
new LocalDate(
startingMonth > endingMonth ? year + 1 : year,
endingMonth, 1)
.PlusMonths(1).PlusDays(-1)));
// Get the ranges that are overlapping with this interval
var results = ranges.Where(p => p.StartDate <= this.EndDate &&
p.EndDate >= this.StartDate)
.ToArray();
// Trim the edges to constrain the results to this interval
if (results.Length > 0)
{
if (results[0].StartDate < this.StartDate)
{
results[0] = new LocalDateInterval(
this.StartDate,
results[0].EndDate);
}
if (results[results.Length - 1].EndDate > this.EndDate)
{
results[results.Length - 1] = new LocalDateInterval(
results[results.Length - 1].StartDate,
this.EndDate);
}
}
return results;
}
}
Using the above code:
var interval = new LocalDateInterval(new LocalDate(2014, 3, 1), // inclusive
new LocalDate(2015, 2, 28)); // inclusive
var subIntervals = interval.GetSubIntervals(4, 11);
This shoud work:
var periods = Periods
.Select(p => new {
p = p,
a = p.StartingDate.Year*12 + p.StartingDate.Month - 1,
b = p.EndingDate.Year*12 + p.EndingDate.Month
}
)
.Select(x => new {
period = x.p,
subperiods =
Enumerable
.Range(x.a, x.b - x.a)
.Select(e => new DateTime(e/12, e%12 + 1, 1))
.Where(d => StartingMonth <= d.Month && d.Month <= EndingMonth)
.GroupBy(i => i.Year)
.Where(g => g.Count() > 1)
.Select(g => new Period {
StartingDate = g.Min(),
EndingDate = g.Max()
})
.Select(p => new Period {
StartingDate = p.StartingDate < x.p.StartingDate ? x.p.StartingDate : p.StartingDate,
EndingDate = (p.EndingDate > x.p.EndingDate ? x.p.EndingDate : p.EndingDate)
.AddMonths(1)
.AddDays(-1)
})
});
UPDATE
According to your image, this would do the trick:
var periods = Periods
.Select(p => new {
p = p,
a = p.StartingDate.Year*12 + p.StartingDate.Month - 1,
b = p.EndingDate.Year*12 + p.EndingDate.Month
}
)
.Select(x => new {
period = x.p,
subperiods =
Enumerable
.Range(x.a, x.b - x.a)
.Select(e => new DateTime(e/12, e%12 + 1, 1))
.Where(d => StartingMonth <= d.Month && d.Month <= EndingMonth)
.GroupBy(i => i.Year)
.Where(g => g.Count() > 1)
.Select(g => g.Select(i => i))
});
ASP.NET using C#
The following are the Quarters for the financial year 2011-12
April 2011 to June2011 - Q1
July2011 to Sep2011 - Q2
Oct2011 to Dec2011 - Q3
Jan2012 to March 2012 - Q4
EDIT:
If i give a date as input then i need the output interms of the Quarter of that month:
Lets consider a date as input is 02-Jan-2012.
then i need the output as Q4
Lets take another date as input: 31May2012.
For this i need the output as Q1
Please help!!
Here is the function
public string GetQuarter(DateTime date)
{
// we just need to check the month irrespective of the other parts(year, day)
// so we will have all the dates with year part common
DateTime dummyDate = new DateTime(1900, date.Month, date.Day);
if (dummyDate < new DateTime(1900, 7, 1) && dummyDate >= new DateTime(1900, 4, 1))
{
return "Q1";
}
else if (dummyDate < new DateTime(1900, 10, 1) && dummyDate >= new DateTime(1900, 7, 1))
{
return "Q2";
}
else if (dummyDate < new DateTime(1900, 1, 1) && dummyDate >= new DateTime(1900, 10, 1))
{
return "Q3";
}
else
{
return "Q4";
}
}
Hope this could help.
static void Main(string[] args)
{
List<DateRange> range = new List<DateRange>();
//temp filling the data
DateTime start = new DateTime(2011, 4, 1);
range.Add(new DateRange() {From=start,To = start.AddMonths(3).AddMilliseconds(-1),Name="Q1"});
start = range.LastOrDefault().To.AddMilliseconds(1);
range.Add(new DateRange() { From = start, To = start.AddMonths(3).AddMilliseconds(-1), Name = "Q2" });
start = range.LastOrDefault().To.AddMilliseconds(1);
range.Add(new DateRange() { From = start, To = start.AddMonths(3).AddMilliseconds(-1), Name = "Q3" });
start = range.LastOrDefault().To.AddMilliseconds(1);
range.Add(new DateRange() { From = start, To = start.AddMonths(3).AddMilliseconds(-1), Name = "Q4" });
var order = range.OrderByDescending(r => r.IsCurrentQuater(DateTime.Now));
foreach (var itm in order)
Console.WriteLine(itm);
}
}
public class DateRange
{
public string Name { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public bool IsCurrentQuater(DateTime date)
{
return date >= From && date <= To;
}
public override string ToString()
{
return string.Format("{0} - {1} to {2}", Name, From, To);
}
}
Regards.