I have a table with Logs and I am counting the Logs per day as follows:
// Count logs by day
IList<DataModel> models = _context.Logs
.Where(x => x.Created >= dateMinimum && x.Created <= dateMaximum)
.GroupBy(x => new { Year = x.Created.Year, Month = x.Created.Month, Day = x.Created.Day })
.Select(x => new { Year = x.Key.Year, Month = x.Key.Month, Day = x.Key.Day, Count = x.Count() })
.AsEnumerable()
.Select(x => new DataModel { Date = new DateTime(x.Year, x.Month, x.Day), LogsCount = x.Count })
.ToList();
// Fill empty days with dates which contains all days in range
models.AddRange(dates.Where(x => !models.Any(y => y.Date == x.Date)).Select(x => new DataModel { Date = x, LogsCount = 0 }));
This is working if I want to count all logs by day independently of the type.
But I would like to count logs by day and type (Error, Warn, Info, ...).
I tried to add x.Type to group but at the end I get only 3 items.
At the moment my DataModel is the following:
public class DataModel
{
public DateTime Date { get; set; }
public Int32 LogsCount { get; set; }
}
But maybe it should be something like:
public class DataModel
{
public DateTime Date { get; set; }
public KeyValuePair<String, Int32> LogsCount { get; set; }
}
Where LogsCount has a string which holds the Type and Int32 which contains the count.
How can I do this?
Might want to consider using entity functions for grouping by date.
Example:
var results = query.GroupBy(r => new
{
SentDate = System.Data.Objects.EntityFunctions.TruncateTime(r.Launch.EmailDeliveredDate),
EventSubTypeID = r.EmailEventSubtypeID
})
.Select(x => new
{
x.Key.SentDate,
x.Key.EventSubTypeID,
NumResults = x.Count()
})
.ToList();
Did you try something like this?
IList<DataModel> models = Logs
.Where(x => x.Created >= dateMinimum && x.Created <= dateMaximum)
.GroupBy(x => new { Year = x.Created.Year, Month = x.Created.Month, Day = x.Created.Day, Type = x.Type })
.Select(x => new { Year = x.Key.Year, Month = x.Key.Month, Day = x.Key.Day, Count = x.Count(), Type = x.Key.Type })
.AsEnumerable()
.Select(x => new DataModel { Date = new DateTime(x.Year, x.Month, x.Day), LogsCount = x.Count, Type = x.Type })
.ToList()
public class DataModel
{
public DateTime Date { get; set; }
public Int32 LogsCount { get; set; }
public string Type { get; set; }
}
Related
I am trying to create a tree view that would essentially break down like so:
- Year
- Month
- Related Item
So we might have the Year 2022, that has several related items within the several months.
I have created the following model:
public class TreeYear
{
public string NodeYear { get; set; }
public DateTime CreatedDateTime { get; set; }
public List<TreeMonth> Months { get; set; }
}
public class TreeMonth
{
public int MonthID { get; set; }
public string MonthName { get; set; }
public quoteSummary QuoteSummary{ get; set; }
}
I have written some code in my controller which currently returns every item like so:
var allQuotes = QuoteSummary.ToList();
var tree = new TreeYear();
foreach (var quote in allQuotes)
{
tree.NodeYear= quote.CreatedTime.Year.ToString();
tree.CreatedDateTime = quote.CreatedTime;
tree.Months = new List<TreeMonth>()
{
new TreeMonth() {
MonthID = quote.CreatedTime.Month,
MonthName = getAbbreviatedName(quote.CreatedTime.Month),
QuoteSummary = quote
}
};
}
But obviously over here you can see that it has all 41 records of which none are grouped up by year.
I thought maybe I could write some linq something like but at the moment incorrect:
var groups = TheResponse.Details
.GroupBy(
d => Int32.Parse(d.NodeYear),
(key, g) => g.GroupBy(
d => d.Months.Select(x => x.MonthID)),
(key2, g2) => g2.GroupBy(d => d.CreatedDateTime)
)
);
Or would I need to change the model for this idea to work?
If I understood your question correctly, then you need to flatten the inner list and then group by months again.
var groups = TheResponse.Details
.GroupBy(d => Int32.Parse(d.NodeYear))
.Select(d => new
{
Year = d.Key,
MonthObj = d.SelectMany(m => m.Months)
.GroupBy(m => m.MonthID)
.Select(x => new
{
MonthID = x.Key,
RelatedItem = x.ToList()
})
});
I have simplified it by using anonymous types, but you can obviously tweek it based on your resp. Model.
I am trying to getting the max enddate of a list object using a group by with the Id and ProductId. But I am getting a Nullable object must have a value." I have tried the HasValue but I get a runtime error? Am I doing this incorrect?
static void Main(string[] args)
{
try
{
List<Data> ListOfData = new List<Data>();
ListOfData.Add(new Data() { Id = 1, ProductId = 5, EndDate = null });
ListOfData.Add(new Data() { Id = 2, ProductId = 6, EndDate = null });
ListOfData.Add(new Data() { Id = 3, ProductId = 4, EndDate = DateTime.Now });
//var ListofUniqueData = ListOfData.GroupBy(r => new { r.Id, r.ProductId })
// .Select(g => g.OrderByDescending(r => r.EndDate.HasValue ? r.EndDate.Value : null ).First())
// .Select(i => new { i.Id, i.ProductId, i.EndDate.HasValue ? i.EndDate.Value : null })
// .ToList();
var ListofUniqueData = ListOfData.GroupBy(r => new { r.Id, r.ProductId })
.Select(g => g.OrderByDescending(r => r.EndDate.Value)
.First())
.Select(i => new { i.Id, i.ProductId, i.EndDate.Value })
.ToList();
}
catch ( Exception ex )
{
ex.ToString();
}
}
class Data
{
public int Id { get; set; }
public int ProductId { get; set; }
public DateTime? EndDate { get; set; }
}
You can use GetValueOrDefault to get the default value of the type if it's not assigned:
var ListofUniqueData = ListOfData.GroupBy(r => new {r.Id, r.ProductId})
.Select(g => g.OrderByDescending(r => r.EndDate.GetValueOrDefault()).First())
.Select(i => new {i.Id, i.ProductId, i.EndDate.GetValueOrDefault()})
.ToList();
I am working on a function which should dedupe (remove duplicates) from a list object. Here is the requirement:
Tradeline is considered as duplicate if it has:
Same account number, account type, date and it's not manual
If you found something then select only those which has
Latest reported date
If same reported date then compare (30,60,90) fields and select tradeline which has a higher value in ANY of the above three attributes
I am having trouble implementing the last bullet point. Here is my code:
public IEnumerable<Tradeline> DedupeTradeline(IEnumerable<Tradeline> tradelines)
{
//split tradeline into manual and non-manual
var tradelineDictionary = tradelines.GroupBy(x => x.Source == "MAN").ToDictionary(x => x.Key, x => x.ToList());
//create list of non-manual tradeline for dedupe logic
var nonManualTradelines = tradelineDictionary.Where(x => x.Key == false).Select(x => x.Value).FirstOrDefault();
var manualTradelines = tradelineDictionary.Where(x => x.Key).Select(x => x.Value).FirstOrDefault();
//check if same reported date is present for dedupe tradelines
var duplicate = nonManualTradelines?.GroupBy(x => new
{
x.ReportedDate,
x.Account,
x.AcctType,
x.Date
}).Any(g => g.Count() > 1);
IEnumerable<Tradeline> dedupe;
if (duplicate != null && (bool) !duplicate)
{
//logic for dedupe tradeline if no same reported date
dedupe = nonManualTradelines.GroupBy(x => new
{
x.Account,
x.AcctType,
x.Date
})
//in case of duplicate tradelines select one with the latest date reported
.Select(x => x.OrderByDescending(o => o.ReportedDate).First());
}
else
{
//logic for dedupe tradeline if same reported date
dedupe = nonManualTradelines?.GroupBy(x => new
{
x.ReportedDate,
x.Account,
x.AcctType,
x.Date
})
.Select();
// Stuck here not sure what to do
}
//append manual tradeline to the output of dedupe tradelines
var response = manualTradelines != null ? (dedupe).Union(manualTradelines) : dedupe;
return response;
}
Tradeline class:
public class Tradeline
{
public string Account { get; set; }
public string AcctType { get; set; }
public string Late30 { get; set; }
public string Late60 { get; set; }
public string Late90 { get; set; }
public string Date { get; set; }
public string ReportedDate { get; set; }
public string Source { get; set; }
}
You can just sort descending by the maximum Latex value. I replaced the peculiar use of Dictionary with a simple and more efficient separation of the two categories.
public static class ObjectExt {
public static int ToInt<T>(this T obj) => Convert.ToInt32(obj);
}
public IEnumerable<Tradeline> DedupeTradeline(IEnumerable<Tradeline> tradelines) {
//split tradeline into manual and non-manual
var nonManualTradelines = new List<Tradeline>();
var manualTradelines = new List<Tradeline>();
foreach (var t in tradelines) {
if (t.Source == "MAN")
manualTradelines.Add(t);
else
nonManualTradelines.Add(t);
}
IEnumerable<Tradeline> dedupe = nonManualTradelines.GroupBy(t => new {
t.Account,
t.AcctType,
t.Date
})
//in case of duplicate tradelines select one with the latest date reported
.Select(tg => tg.OrderByDescending(t => t.ReportedDate).ThenByDescending(t => Math.Max(t.Late90.ToInt(), Math.Max(t.Late60.ToInt(), t.Late30.ToInt()))).First());
//append manual tradeline to the output of dedupe tradelines
return dedupe.Union(manualTradelines);
}
Model:
public class Ticket {
public Ticket();
public int Id { get; set; }
public virtual TicketUrgency TicketUrgency { get; set; }
public int UrgencyId { get; set; }
}
public class TicketUrgency {
public TicketUrgency();
[Key]
public int Id { get; set; }
[MaxLength(50)]
[Required]
public string Name { get; set; }
public ICollection<Ticket> Tickets { get; set; }
}
I have the following linq statement:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => x.UrgencyId)
.Select(g => new {
id = g.Key,
count = g.Count(),
name = g.FirstOrDefault(u => u.UrgencyId == g.Key).TicketUrgency.Name
});
I want to Group Entities by UrgencyId and then return the Key (UrgencyId), and also count of the items in a single group and show the name of the Urgency.
When I run it, the query just gets hung up without any exceptions.
This should work, doing it the other way around, by retrieving all TicketUrgencies first and grouping it.
Entities.Include(e => e.Tickets)
.GroupBy(t => t.Id)
.Select(g => new {
id = g.Key,
name = g.FirstOrDefault().Name,
count = g.FirstOrDefault().Tickets.Count()
});
Very simple. Just try this :
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => new {UrgencyId = x.UrgencyId ,
Name = x.TicketUrgency.Name})
.Select(x=> new { UrgencyId = x.Key.UrgencyId,
Name = x.Key.Name,
Count = x.Count()});
Since you are grouping by UrgencyId, you know all members of g have the same id as the Key, so to pick up the name just pull the first one. You also know g isn't empty because that wouldn't make a group:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => x.UrgencyId)
.Select(g => new {
id = g.Key,
name = g.First().TicketUrgency.Name
count = g.Count(),
});
You could group by those two properties:
var model = Entities
.Include(x => x.TicketUrgency)
.GroupBy(x => new{ x.UrgencyId, x.TicketUrgency.Name })
.Select(g => new {
id = g.Key.UrgencyId,
count = g.Count(),
name = g.Key.Name
});
Another way could be, as #ASpirin suggested,starting the query from TickerUrgency:
var result= TicketUrgencies.Include(t=>t.Tickets)
.Where(t=>t.Tickets.Any())
.Select(t=> new {id=t.Id,name=t.Name, count= t.Tickets.Count()})
Im trying to configure kendo chart to display data of my model:
public class CallByCountry
{
public DateTime Date { get; set; }
public int Month { get; set; }
public int Year { get; set; }
public DateTime Period { get { return new DateTime(Year, Month, 1); } }
public string Country { get; set; }
public int CallsCount { get; set; }
}
Sample data:
Month Year Country CallsCount
7 2015 USA 5
8 2015 USA 3
8 2015 UK 9
...
My chart:
#(Html.Kendo().Chart<CallByCountry>()
.Name("CallByCountry")
.ChartArea(chartArea => chartArea
.Background("transparent")
)
.DataSource(dataSource => dataSource
.Read(read => read.Action("CallsByCountry", "Reports"))
.Group(group => { group.Add(model => model.Country); })
.Sort(sort => sort.Add(model => new { model.Period}).Ascending())
)
.Series(series =>
{
series.Line(model => model.CallsCount)
.Name("#= group.value #").Style(ChartLineStyle.Smooth);
})
.Legend(legend => legend
.Position(ChartLegendPosition.Bottom)
)
.ValueAxis(axis => axis.Numeric().Labels(l => l.Format("{0:n0}")).MajorUnit(1))
.CategoryAxis(axis => axis
.Categories(model => model.Period)
.Date().BaseUnit(ChartAxisBaseUnit.Months)
.Labels(lbl => lbl.Format("{0:MM/yyyy}"))
)
.Tooltip(tooltip => tooltip
.Visible(true)
.Template("#= series.name #: #= value #"))
)
Controller:
public ActionResult CallsByCountry()
{
List<CallByCountry> callsByCountry = new List<CallByCountry>();
foreach (var call in _callsRepo.GetAll().ToList())
{
var callByCountry = new CallByCountry();
callByCountry.Date = call.StartDate.Date;
callByCountry.Country = _contactRepo.Find(call.ContactID).Country;
callsByCountry.Add(callByCountry);
}
IEnumerable<CallByCountry> data = callsByCountry.GroupBy(i => new { i.Date.Month, i.Date.Year, i.Country })
.Select(group => new CallByCountry()
{
Country = group.Key.Country,
Month = group.Key.Month,
Year = group.Key.Year,
CallsCount = group.Count()
}).OrderBy(x => x.Period);
return Json(data, JsonRequestBehavior.AllowGet);
}
However, I get incorrect representation of my data. The category X-axis shows only one month "7/2015", and some of data for August is displayed in July category.
I guess that can be json parsing issue, which occurs with dates, but im using only month and year.
Please advise, what am I doing wrong?
I'd appreciate any help!
I've adjusted a few things.
I would recommend instead of doing the Period property on your model, do something like this (creating a DateTime rather than a string):
public DateTime Date { get { return new DateTime(Year, Month, 1); } }
This will allow you to leverage the .Date() builder on the CategoryAxis of the grid, like so:
.CategoryAxis(axis => axis
.Categories(model => model.Date)
.Date().BaseUnit(ChartAxisBaseUnit.Months)
.Labels(lbl => lbl.Format("{0:MM/yyyy}"))
)
There also seems to be an issue with the sorting of the data. I adjusted your .Sort() to be
.Sort(sort => sort.Add(model => new {model.Date}).Ascending())
but I noticed the data still was not appearing properly. In your CallsByCountry() action method, sort the data before you return it.
A full example: https://github.com/mmillican/KendoMvcExamples/commit/9ebaa7c4b5c2ddd2a65890cf3d5d77a484d8a3aa