Related
How can I show month names in my view instead of month numbers?
Action Method:
public ActionResult MyPerformance()
{
int year = DateTime.Now.Year;
DateTime firstDay = new DateTime(year, 1, 1);
DateTime lastDay = new DateTime(year, 12, 31).AddDays(1).AddSeconds(-1);
var result = db.Chats
.Where(c => System.Data.Entity.DbFunctions.TruncateTime(c.MSTChatCreatedDateTime) >= firstDay &&
System.Data.Entity.DbFunctions.TruncateTime(c.MSTChatCreatedDateTime) <= lastDay)
.Select(x => new { x.MSTChatCreatedDateTime })
.ToList()
.GroupBy(c => new
{
Year = c.MSTChatCreatedDateTime.ToCleanDateTime().Year,
Month = c.MSTChatCreatedDateTime.ToCleanDateTime().Month
})
.Select(c => new ReportVM
{
Title = string.Format("{0}", c.Key.Month), //chart x Axis value.
ChatCountCreatdDate = c.Count() //chart y Axis value.
})
.ToList();
return View(result);
}
Data being reflected is correct in the View, but I am not able to render the month names in the view.
See the attached Image showing the month numbers.
Thank you!
You can format in "MMM" which display name, here is your code
public ActionResult MyPerformance()
{
int year = DateTime.Now.Year;
DateTime firstDay = new DateTime(year, 1, 1);
DateTime lastDay = new DateTime(year, 12, 31).AddDays(1).AddSeconds(-1);
var result = db.Chats
.Where(c => System.Data.Entity.DbFunctions.TruncateTime(c.MSTChatCreatedDateTime) >= firstDay &&
System.Data.Entity.DbFunctions.TruncateTime(c.MSTChatCreatedDateTime) <= lastDay)
.Select(x => new { x.MSTChatCreatedDateTime })
.ToList()
.GroupBy(c => new
{
Year = c.MSTChatCreatedDateTime.ToCleanDateTime().Year,
Month = c.MSTChatCreatedDateTime.ToCleanDateTime().Month
})
.Select(c => new ReportVM
{
Title = c.MSTChatCreatedDateTime.ToCleanDateTime().ToString("MMMM"), //chart x Axis value.
ChatCountCreatdDate = c.Count() //chart y Axis value.
})
.ToList();
return View(result);
}
How would I get this query to get the monthly count data for the past 12 months? I don't want to hard code the range, I want to use the current date DateTime.Now and get all the data for the past 12 months from that. I am trying to avoid adding a calendar table to the database and do this just using LINQ.
Some months might not have any data but I still need a count of 0 for those.
For example. If my data contains
Date Count
12/2/2013, 4
10/1/2014, 1
11/5/2014, 6
The results should be, using the current date of 11/9/2014
11/2013, 0
12/1013, 4
1/2014, 0
2/2014, 0
3/2014, 0
4/2014, 0
5/2014, 0
6/2014, 0
7/2014, 0
8/2014, 0
9/2014, 0
10/2014, 1
11/2014, 6
I can't get it to work. I think it's how I'm using Range but I'm not sure.
TimeSpan ts = new TimeSpan(365, 0, 0, 0);
DateTime yearAgo = DateTime.Now.Subtract(ts);
var changesPerYearAndMonth =
from year in Enumerable.Range(yearAgo.Year, 1)
from month in Enumerable.Range(1, 12)
let key = new { Year = year, Month = month }
join revision in list on key
equals new { revision.LocalTimeStamp.Year,
revision.LocalTimeStamp.Month } into g
select new { GroupCriteria = key, Count = g.Count() };
I have modified the answer from this this link as a starting point.
Linq: group by year and month, and manage empty months
I just found this article that is the same question but unanswered
Linq - group by datetime for previous 12 months - include empty months
To get the past twelve months, use
var now = DateTime.Now;
var months = Enumerable.Range(-12, 12)
.Select(x => new {
year = now.AddMonths(x).Year,
month = now.AddMonths(x).Month });
To be safe you should first move 'now' to the start of the month to avoid any end-of-month effects with AddMonth.
var now = DateTime.Now;
now = now.Date.AddDays(1-now.Day);
Complete example:-
var list = new [] {
new { LocalTimeStamp = DateTime.Parse("12/2/2013"), count = 4},
new { LocalTimeStamp = DateTime.Parse("10/1/2014"), count = 1 },
new { LocalTimeStamp = DateTime.Parse("11/5/2014"), count = 6}
};
var now = DateTime.Now;
now = now.Date.AddDays(1-now.Day);
var months = Enumerable.Range(-12, 13)
.Select(x => new {
year = now.AddMonths(x).Year,
month = now.AddMonths(x).Month });
var changesPerYearAndMonth =
months.GroupJoin(list,
m => new {month = m.month, year = m.year},
revision => new { month = revision.LocalTimeStamp.Month,
year = revision.LocalTimeStamp.Year},
(p, g) => new {month = p.month, year = p.year,
count = g.Sum(a => a.count)});
foreach (var change in changesPerYearAndMonth)
{
Console.WriteLine(change.month + " " + change.year +" " + change.count);
}
You don't need a 3-way join, you just need to filter your data before grouping.
1) Query expression syntax
// since your list item type was not posted, anyway same access as your LocalTimeStamp property
list = new List<DateTime>();
DateTime aYearAgo = DateTime.Now.AddYears(-1);
var dateslastYear = from date in list
where date > aYearAgo
group date by new { date.Year, date.Month } into g
select new { GroupCriteria = g.Key, Count = g.Count() };
2) Chained
dateslastYear = list.Where (d=>d>aYearAgo)
.GroupBy (date=>new{date.Year, date.Month });
3) If you want grouping by year/month pairs, including records of not existent entries, and also omitting those pairs that are older than a year occurring with the joined Enumerable.Range call:
var thisYearPairs = from m in Enumerable.Range(1, DateTime.Now.Month)
select new { Year = DateTime.Now.Year, Month = m };
var lastYearPairs = from m in Enumerable.Range(DateTime.Now.Month, 12 - DateTime.Now.Month + 1)
select new { Year = DateTime.Now.Year - 1, Month = m };
var ymOuter = from ym in thisYearPairs.Union(lastYearPairs)
join l in list on new { ym.Year, ym.Month } equals new { l.Year, l.Month } into oj
from p in oj.DefaultIfEmpty()
select new { a = ym, b = p == null ? DateTime.MinValue : p };
var ymGroup = from ym in ymOuter
group ym by ym into g
select new { GroupCriteria = g.Key.a, Count = g.Key.b == DateTime.MinValue ? 0 : g.Count() };
You are taking the range for the 12 months of last year only but you actually want the last twelve months.
You can do this using a Enumerable.Range and the AddMonths method:
var changesPerYearAndMonth =
from month in Enumerable.Range(0, 12)
let key = new { Year = DateTime.Now.AddMonths(-month).Year, Month = DateTime.Now.AddMonths(-month).Month }
join revision in list on key
equals new
{
revision.LocalTimeStamp.Year,
revision.LocalTimeStamp.Month
} into g
select new { GroupCriteria = key, Count = g.Count() };
public int YearDiff(DateTime a, DateTime b)
{
return (int) Math.Floor((a.Year + a.Month / 100.0 + a.Day / 10000.0) - (b.Year + b.Month / 100.0 + b.Day / 10000.0));
}
I have the following query which works fine:
var bands = new List<TimeBand>()
{
new TimeBand(){Region = 10,PeriodId = 5,StartDate = new DateTime(2013, 04, 01),EndDate = new DateTime(2014, 05, 31),DayName = "Friday",StartTime = "00:00",EndTime = "07:00"},
new TimeBand(){Region = 10,PeriodId = 5,StartDate = new DateTime(2013, 04, 01),EndDate = new DateTime(2013, 05, 31),DayName = "Friday",StartTime = "07:00",EndTime = "00:00"},
new TimeBand(){Region = 10,PeriodId = 4,StartDate = new DateTime(2013, 06, 01),EndDate = new DateTime(2013, 08, 31),DayName = "Saturday",StartTime = "20:00",EndTime = "00:00"}
};
var query = (from x in bands
group x by new {x.Region, x.DayName}
into grp
select new TimeBand()
{
Region = grp.Key.Region,
DayName = grp.Key.DayName,
StartDate = grp.Min(x => x.StartDate),
EndDate = grp.Max(x => x.EndDate)
}).ToList();
But as I group the results by Region and Dayname I am not getting the other columns in my result i.e StartTime and EndTime.
If this was a SQL query I would have used this grouped results in a subquery and get the other columns as well.
Is there any way of modifying this so I also get the properties which are not included in the group by statement.
Thanks
After grouping source items you have sequence of groups. How you will project these groups is up to you. Usually you select grouping keys and some aggregated values on each group (that is how SQL works). But you can select each group itself, or first item from each group, or some value from last group item:
from b in bands
group b by new { b.Region, b.DayName } into g
select new {
g.Key.Region,
g.Key.DayName,
StartDate = g.Min(x => x.StartDate),
EndDate = g.Max(x => x.EndDate),
AllBandsFromGroup = g,
FirstBand = g.First(),
LastBandPeriod = g.Last().Period
}
Just select out the whole group if you want the keys as well as all of the values for all of the items in the groups:
select grp;
The other option is to select out a sequence of all values of the columns that you want from a group:
select new TimeBand()
{
Region = grp.Key.Region,
DayName = grp.Key.DayName,
StartDates = grp.Select(x => x.StartDate),
EndDates = grp.Select(x => x.EndDate)
}
You'd only need to do this if you didn't want to pull down the data for some number of other columns. If you want all of the columns, just use the first option.
var query = (from x in bands
group x by new { x.Region, x.DayName }
into grp
select new
{
Region = grp.Key.Region,
DayName = grp.Key.DayName,
MinStartDate = grp.Min(x => x.StartDate),
AllStartDates = grp.Select(k => k.StartDate).ToList(),
EndDate = grp.Max(x => x.EndDate),
AllEndDates = grp.Select(k => k.EndDate).ToList(),
}).ToList();
It seems your are trying to find the minimum start date and time and maximum end date and time for each group. This might be much easier if you combine date and time into a single property.
Otherwise you will need a IComparer<TimeBand> for start date and time and another one for end date and time.
If you don't want to combine date and time you can write methods to get the combined values:
public DateTime GetStart()
{
int hour = int.Parse(StartTime.Substring(0, 2));
int minute = int.Parse(StartTime.Substring(3, 2));
return new DateTime(StartDate.Year, StartDate.Month, StartDate.Day, hour, minute, 0);
}
public DateTime GetEnd()
{
int hour = int.Parse(EndTime.Substring(0, 2));
int minute = int.Parse(EndTime.Substring(3, 2));
return new DateTime(EndDate.Year, EndDate.Month, EndDate.Day, hour, minute, 0);
}
Now you can group the bands and for each group find the minimum start and maximum end:
var query = from x in bands
group x by new
{
x.Region,
x.DayName
}
into grp
select new TimeBand()
{
Region = grp.Key.Region,
DayName = grp.Key.DayName,
StartDate = grp.Min(x => x.StartDate),
StartTime = grp.Min(x => x.GetStart()).ToShortTimeString(),
EndDate = grp.Max(x => x.EndDate),
EndTime = grp.Max(x => x.GetEnd()).ToShortTimeString(),
};
This is however not very elegant. I would prefer to combine date and time in the first place.
I need list of weeks with starting and ending dates by giving int year and int month,
Example Result,
Week1 = 7/1/2012 to 7/1/2012
Week2 = 7/2/2012 to 7/8/2012
Week3 = 7/9/2012 to 7/15/2012
Week4 = 7/16/2012 to 7/22/2012
Week5 = 7/23/2012 to 7/29/2012
Week6 = 7/30/2012 to 7/31/2012
Something like this should work:
// using System.Globalization;
var calendar = CultureInfo.CurrentCulture.Calendar;
var firstDayOfWeek = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
var weekPeriods =
Enumerable.Range(1, calendar.GetDaysInMonth(year, month))
.Select(d =>
{
var date = new DateTime(year, month, d);
var weekNumInYear = calendar.GetWeekOfYear(date, CalendarWeekRule.FirstDay, firstDayOfWeek);
return new { date, weekNumInYear };
})
.GroupBy(x => x.weekNumInYear)
.Select(x => new { DateFrom = x.First().date, To = x.Last().date })
.ToList();
Of course you can change the Culture (here I have used the CurrentCulture).
check this one and edit according to your need
// Get the weeks in a month
DateTime date = DateTime.Today;
// first generate all dates in the month of 'date'
var dates = Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month)).Select(n => new DateTime(date.Year, date.Month, n));
// then filter the only the start of weeks
var weekends = (from d in dates
where d.DayOfWeek == DayOfWeek.Monday
select d).ToList();
foreach (var d in weekends)
{
Console.WriteLine(d);
}
Console.Write(weekends);
Console.ReadLine();
hope it help you.
you can also take a look here for other stuff HERE
The following NHibernate QueryOver query is counting the number of applications for each month, within a given date range.
However, I don't get any results for months that don't have any applications in them but I want to actually have Count = 0 returned for those months.
So how would I change the query to return a row as well for months that don't have any applications in them?
DateTimeOffset endDate = DateTimeOffset.Now;
DateTimeOffset startDate = endDate.AddMonths(-12);
var result = Session.QueryOver<Application>()
.WhereRestrictionOn(c => c.SubmissionDate).IsBetween(startDate).And(endDate)
.SelectList(list => list
.Select(Projections.SqlGroupProjection(
"YEAR(SubmissionDate) As [Year]",
"YEAR(SubmissionDate)",
new[] { "YEAR" },
new IType[] { NHibernateUtil.Int32 }))
.Select(Projections.SqlGroupProjection(
"MONTH(SubmissionDate) As [Month]",
"MONTH(SubmissionDate)",
new[] { "MONTH" },
new IType[] { NHibernateUtil.Int32 }))
.SelectCount(x => x.Id))
.OrderBy(Projections.SqlFunction(
"YEAR",
NHibernateUtil.Int32,
Projections.Property<Application>(item => item.SubmissionDate))).Asc
.ThenBy(Projections.SqlFunction(
"MONTH",
NHibernateUtil.Int32,
Projections.Property<Application>(item => item.SubmissionDate))).Asc
.List<object[]>()
.Select(n => new
{
Year = n[0],
Month = n[1],
Count = (int)n[2]
}));
Update: taking your idea with DateTime.AddMonths() it gets even shorter
DateTime lastMonth = startdate;
var unionresults = result.SelectMany(r =>
{
var actualDate = new DateTime(r.Year, r.Month, 1);
var results = Enumerable.Repeat(1, Months)
.Select(i => lastMonth.AddMonths(i))
.TakeWhile(date => date < actualDate)
.Select(date => new { Year = date.Year, Month = date.Month, Count = 0 })
.Concat(new[] { r });
lastMonth = actualDate;
return results;
});
Original:
i think you have to add that data after the query. here an example using linq to fill in missing months
var result = <query>;
int lastMonth = 1;
var unionresults = result.SelectMany(r =>
{
var results = new[] { r }.AsEnumerable();
if (lastMonth > r.Month)
{
results = Enumerable.Range(lastMonth, 12 - lastMonth).Select(month => new { Year = r.Year, Month = month, Count = 0 })
.Concat(Enumerable.Range(1, r.Month).Select(month => new { Year = r.Year, Month = month, Count = 0 }))
.Concat(results);
}
else if (lastMonth < r.Month)
{
results = Enumerable.Range(lastMonth, r.Month - lastMonth)
.Select(month => new { Year = r.Year, Month = month, Count = 0 })
.Concat(results);
}
lastMonth = r.Month + 1;
if (lastMonth > 12)
{
lastMonth = 1;
}
return results;
});
It cannot be done with a few simple changes. The SQL query that is generated by your QueryOver() cannot count what does not exist in the first place.
You could probably do it with a UNION or a JOIN using a virtual/temporary table (depending on the DBMS) but that would make the query overly complicated.
I suggest adding a loop after your query that iterates through the list, copies the elements to a new list and adds any non-existing months to that new list. Something like this:
class YearMonthCount
{
public int Year { get; set; }
public int Month { get; set; }
public int Count { get; set; }
}
// Start and End dates
DateTime startDate = new DateTime(2011, 9, 1);
DateTime endDate = new DateTime(2012, 6, 1);
// this would be a sample of the QueryOver() result
List<YearMonthCount> result = new List<YearMonthCount>();
result.Add(new YearMonthCount { Year = 2011, Month = 10, Count = 2 });
result.Add(new YearMonthCount { Year = 2011, Month = 11, Count = 3 });
result.Add(new YearMonthCount { Year = 2012, Month = 1, Count = 4 });
result.Add(new YearMonthCount { Year = 2012, Month = 2, Count = 1 });
result.Add(new YearMonthCount { Year = 2012, Month = 4, Count = 1 });
result.Add(new YearMonthCount { Year = 2012, Month = 5, Count = 1 });
int i = 0;
List<YearMonthCount> result2 = new List<YearMonthCount>();
// iterate through result list, add any missing entry
while (startDate <= endDate)
{
bool addNewEntry = true;
// check to avoid OutOfBoundsException
if (i < result.Count)
{
DateTime listDate = new DateTime(result[i].Year, result[i].Month, 1);
if (startDate == listDate)
{
// entry is in the QueryOver result -> add this
result2.Add(result[i]);
i++;
addNewEntry = false;
}
}
if (addNewEntry)
{
// entry is not in the QueryOver result -> add a new entry
result2.Add(new YearMonthCount {
Year = startDate.Year, Month = startDate.Month, Count = 0 });
}
startDate = startDate.AddMonths(1);
}
This could probably be done more elegantly but it gets the job done.
Thanks to all the answers, this is how I ended up doing it:
DateTime endDate = DateTime.Now;
DateTime startDate = endDate.AddMonths(-Months);
var result = Session.QueryOver<Application>()
.WhereRestrictionOn(c => c.SubmissionDate).IsBetween(startDate).And(endDate)
.SelectList(list => list
.Select(Projections.SqlGroupProjection(
"YEAR(SubmissionDate) As [Year]",
"YEAR(SubmissionDate)",
new[] { "YEAR" },
new IType[] { NHibernateUtil.Int32 }))
.Select(Projections.SqlGroupProjection(
"MONTH(SubmissionDate) As [Month]",
"MONTH(SubmissionDate)",
new[] { "MONTH" },
new IType[] { NHibernateUtil.Int32 }))
.SelectCount(x => x.Id))
.List<object[]>()
.Select(n => new
{
Year = (int)n[0],
Month = (int)n[1],
Count = (int)n[2]
}).ToList();
var finalResult = result
.Union(
Enumerable.Range(0, Months - 1).Select(n => new
{
Year = startDate.AddMonths(n).Year,
Month = startDate.AddMonths(n).Month,
Count = 0
})
.Where(n => !result.Any(r => r.Year == n.Year && r.Month == n.Month)))
.OrderBy(n => n.Year).ThenBy(n => n.Month);