Grouping week of the month that is input to linechart - c#

I have a problem when grouping weeks from input months,
the results I get are always like this
{name: "Pembunuhan", data: [1,4]}
it should be the result I want like this
{name: "Pembunuhan", data: [1,0,0,4]}
this is my code
var dateNya = DateTime.Today;
var bln = int.Parse(month);
var mstrKategori = context.master_kategori.OrderBy("id ASC").ToList();
var joinnya = (from ls in context.list_dokumen join ktgr in context.master_kategori on ls.kategori equals ktgr.id
where ls.polda_id != null
select new
{
tgl_laporan = ls.tgl_laporan,
idKategori = ktgr.id,
week = ls.week,
month = ls.month,
year = ls.year
}).ToArray();
foreach (var itemktgr in mstrKategori)
{
var tes2 = joinnya.Where(i => i.idKategori == itemktgr.id).Where(a => a.month == bln).Where(o => o.year == dateNya.Year)
.GroupBy(row => new { week = row.week ?? 0 })
.Select(g => new
{
week = g.Key.week,
couny = g == null ? 0: g.Count()
})
.ToList();
tes2.ToList().ForEach(p => lineChartList.Add(new DataChart {name = itemktgr.nama2, data = p.couny}));
}
var result = lineChartList.GroupBy(x => new { x.name })
.Select(b => new DataChartTrending2
{
data = b.Select(bn => bn.data).ToList(),
name = (b.Key.name == null) ? "Lainnya" : b.Key.name
}).ToList();

The GroupBy clause won't create empty groups for weeks that have no matching records.
Use GroupJoin to perform an outer join on week indices, meaning that you will get a group for each week index, even indices that no record in tes2 matched:
var weekIds = Enumerable.Range(0, 4); // assuming your weeks are 0, 1, 2, 3
var tes2 = joinnya
.Where(i => i.idKategori == itemktgr.id)
.Where(a => a.month == bln)
.Where(o => o.year == dateNya.Year)
var countPerWeek = weekIds.GroupJoin(
tes2,
weekId => weekId,
row => row.week,
(week, weekGroup) => weekGroup.Count()
);
For each week, it will get you the number of matching records, including zeroes for weeks that don't have a matching record.
Alternative syntax:
var countPerWeek =
from weekId in weekIds
join row in tes2 on weekId equals row.week into weekGroup
select weekGroup.Count();

Related

LINQ Query Multiple Group and count of latest record - Oracle DB

I tried to divided Linq queries into 3 (total, success, fail) but so far "Total" Linq query is working fine. Please help me to get "Success", "Fail" columns (it has mulitple statuses and we have to check the last column of each transaction and destination)
Note: you need to group by ProcessTime, TransactionId, Destination and check last column whether it is success or Fail then apply count (we are using oracle as backend)
LINQ for Total count
var query = (from filetrans in context.FILE_TRANSACTION
join route in context.FILE_ROUTE on filetrans.FILE_TRANID equals route.FILE_TRANID
where
filetrans.PROCESS_STRT_TIME >= fromDateFilter && filetrans.PROCESS_STRT_TIME <= toDateFilter
select new { PROCESS_STRT_TIME = DbFunctions.TruncateTime((DateTime)filetrans.PROCESS_STRT_TIME), filetrans.FILE_TRANID, route.DESTINATION }).
GroupBy(p => new { p.PROCESS_STRT_TIME, p.FILE_TRANID, p.DESTINATION });
var result = query.GroupBy(x => x.Key.PROCESS_STRT_TIME).Select(x => new { x.Key, Count = x.Count() }).ToDictionary(a => a.Key, a => a.Count);
Check this solution. If it gives wrong result, then I need more details.
var fileTransQuery =
from filetrans in context.AFRS_FILE_TRANSACTION
where accountIds.Contains(filetrans.ACNT_ID) &&
filetrans.PROCESS_STRT_TIME >= fromDateFilter && filetrans.PROCESS_STRT_TIME <= toDateFilter
select filetrans;
var routesQuery =
from filetrans in fileTransQuery
join route in context.AFRS_FILE_ROUTE on filetrans.FILE_TRANID equals route.FILE_TRANID
select route;
var lastRouteQuery =
from d in routesQuery.GroupBy(route => new { route.FILE_TRANID, route.DESTINATION })
.Select(g => new
{
g.Key.FILE_TRANID,
g.Key.DESTINATION,
ROUTE_ID = g.Max(x => x.ROUTE_ID)
})
from route in routesQuery
.Where(route => d.FILE_TRANID == route.FILE_TRANID && d.DESTINATION == route.DESTINATION && d.ROUTE_ID == route.ROUTE_ID)
select route;
var recordsQuery =
from filetrans in fileTransQuery
join route in lastRouteQuery on filetrans.FILE_TRANID equals route.FILE_TRANID
select new { filetrans.PROCESS_STRT_TIME, route.CRNT_ROUTE_FILE_STATUS_ID };
var result = recordsQuery
.GroupBy(p => DbFunctions.TruncateTime((DateTime)p.PROCESS_STRT_TIME))
.Select(g => new TrendData
{
TotalCount = g.Sum(x => x.CRNT_ROUTE_FILE_STATUS_ID != 7 && x.CRNT_ROUTE_FILE_STATUS_ID != 8 ? 1 : 0)
SucccessCount = g.Sum(x => x.CRNT_ROUTE_FILE_STATUS_ID == 7 ? 1 : 0),
FailCount = g.Sum(x => failureStatus.Contains(x.CRNT_ROUTE_FILE_STATUS_ID) ? 1 : 0),
Date = g.Min(x => x.PROCESS_STRT_TIME)
})
.OrderBy(x => x.Date)
.ToList();

LINQ query to retrieve pivoted data taking too long

I am working on a LINQ query which includes some pivot data as below
var q = data.GroupBy(x => new
{
x.Med.Name,
x.Med.GenericName,
}).ToList().Select(g =>
new SummaryDto
{
Name= g.Key.Name,
GenericName = g.Key.GenericName,
Data2012 = g.Where(z => z.ProcessDate.Year == 2012).Count(),
Data2013 = g.Where(z => z.ProcessDate.Year == 2013).Count(),
Data2014 = g.Where(z => z.ProcessDate.Year == 2014).Count(),
Data2015 = g.Where(z => z.ProcessDate.Year == 2015).Count(),
Data2016 = g.Where(z => z.ProcessDate.Year == 2016).Count(),
Data2017 = g.Where(z => z.ProcessDate.Year == 2017).Count(),
TotalCount = g.Count(),
}).AsQueryable();
return q;
The above LINQ takes too long as it queries grp q.Count()*6 times. If there are 10000 records, then it queries 60000 times
Is there a better way to make this faster?
Add year to the group key, then group again, and harvest per-group counts:
return data.GroupBy(x => new {
x.Med.Name
, x.Med.GenericName
, x.ProcessDate.Year
}).Select(g => new {
g.Key.Name
, g.Key.GenericName
, g.Key.Year
, Count = g.Count()
}).GroupBy(g => new {
g.Name
, g.GenericName
}).Select(g => new SummaryDto {
Name = g.Key.Name
, GenericName = g.Key.GenericName
, Data2012 = g.SingleOrDefault(x => x.Year == 2012)?.Count ?? 0
, Data2013 = g.SingleOrDefault(x => x.Year == 2013)?.Count ?? 0
, Data2014 = g.SingleOrDefault(x => x.Year == 2014)?.Count ?? 0
, Data2015 = g.SingleOrDefault(x => x.Year == 2015)?.Count ?? 0
, Data2016 = g.SingleOrDefault(x => x.Year == 2016)?.Count ?? 0
, Data2017 = g.SingleOrDefault(x => x.Year == 2017)?.Count ?? 0
, TotalCount = g.Sum(x => x.Count)
}).AsQueryable();
Note: This approach is problematic, because year is hard-coded in the SummaryDto class. You would be better off passing your DTO constructor an IDictionary<int,int> with counts for each year. If you make this change, the final Select(...) would look like this:
.Select(g => new SummaryDto {
Name = g.Key.Name
, GenericName = g.Key.GenericName
, TotalCount = g.Sum(x => x.Count)
, DataByYear = g.ToDictionary(i => i.Year, i => i.Count)
}).AsQueryable();
I suggest grouping inside the group by year and then converting to a dictionary to access the counts. Whether it is faster to group with year first and then count in-memory depends on the distribution of the initial grouping, but with the database it may depend on how efficiently it can group by year, so I would test to determine which seems fastest.
In any case grouping by year after the initial grouping is about 33% faster than your query in-memory, but again it is vastly dependent on the distribution. As the number of initial groups increase, the grouping by Year queries slow down to match the original query. Note that the original query without any year counts is about 1/3 the time.
Here is grouping after the database grouping:
var q = data.GroupBy(x => new {
x.Med.Name,
x.Med.GenericName,
}).ToList().Select(g => {
var gg = g.GroupBy(d => d.ProcessDate.Year).ToDictionary(d => d.Key, d => d.Count());
return new SummaryDto {
Name = g.Key.Name,
GenericName = g.Key.GenericName,
Data2012 = gg.GetValueOrDefault(2012),
Data2013 = gg.GetValueOrDefault(2013),
Data2014 = gg.GetValueOrDefault(2014),
Data2015 = gg.GetValueOrDefault(2015),
Data2016 = gg.GetValueOrDefault(2016),
Data2017 = gg.GetValueOrDefault(2017),
TotalCount = g.Count(),
};
}).AsQueryable();

C# join items which are the same with lambda expression

With the following code how would I join items together.
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId)
.Select(x => x.ShipmentItems)
.ToList();
So there may be two or more shipments for the order id.
Each shipment may contain identical items.
Need a nudge in the right direction
Have tried
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId)
.Select(x => x.ShipmentItems.Join(x.ShipmentItems
.Where(y => y.ShipmentId == shipment.Id)))
.ToList();
and
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId)
.Select(x => x.ShipmentItems.GroupBy(y => y.Id))
.ToList();
and
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId)
.Select(x => x.ShipmentItems.Distinct())
.ToList();
Here is the code I'm using for output the items:
foreach (var shipmentItem in shippedItems)
{
System.Diagnostics.Debug.WriteLine("item = " + shipmentItem);
System.Diagnostics.Debug.WriteLine("item = " + shipmentItem.OrderItemId);
}
The above output produces:
shippedItemsList count = 9 item =
System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356077
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356078
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356079
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356077
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356078
item =
System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356079
item =
System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356077
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356079
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356080
So the excepted output should be:
shippedItemsList count = 4
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356077
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356079
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356078
item = System.Data.Entity.DynamicProxies.ShipmentItem_18BEAAFA747B42988EC4CB25D967298CC6736AF528389FC98E81143F7D629631
item = 356080
The above output is from using var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId).SelectMany(x => x.ShipmentItems).ToList();
NOTE: shipment item is ICollection<ShipmentItem> Shipment.ShipmentItems when I hover it in visual studio
UPDATED senario:
So let's say that the order has 3 items(a = 3, b= 5, c = 3), Now 1 shipment is sent with items(a = 1, b = 2, c = 0), Now a second shipment is sent with items(a = 1, b = 1, c = 1). I would like the quantity of items from both shipments. So I expect items(a = 2, b = 3, c = 1), I currently get items(a = 1, b = 2) + items(a = 1, b = 1, c = 1). So the list of shipment items I loop through are appearing more than once.
I thought maybe union but not sure how to put it together :/
UPDATE MY SOLUTION
I could not manage to accomplish this using lambda expression, so I
had to do the following solution to get the result I wanted. I hope
this helps others looking for workarounds.
//calculate quantity of total shipped items from each shipment
List<ShipmentItem> alreadyShippedItemsList = new List<ShipmentItem>();
Dictionary<int, int> alreadyShippedItems = new Dictionary<int, int>();
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null)
.SelectMany(x => x.ShipmentItems)
.ToList();
//create a list of shipment items
for (int i = 1; i <= shipmentsList.Count - 1; i++)
{
var si = shipmentsList[i];
var sii = si.ShipmentItems.ToList();
foreach (var item in sii)
{
var itemInList = alreadyShippedItemsList.Where(x => x.OrderItemId == item.OrderItemId).FirstOrDefault();
int sum = 0;
//create a list of shipment items and check for duplicate items
if (itemInList == null)
{
alreadyShippedItems.Add(item.OrderItemId, item.Quantity);
alreadyShippedItemsList.Add(item);
}
else
{
//if duplicate item is found update the quantity in the dictionary
sum = itemInList.Quantity + item.Quantity;
alreadyShippedItems[item.OrderItemId] = sum;
}
}
}
You need to use SelectMany, which flattens a list
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId)
.SelectMany(x => x.ShipmentItems)
.ToList();
You are using Distinct in the wrong place
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId)
.Select(x => x.ShipmentItems).Distinct()
.ToList();
Either override Equals function for the ShipmentItems class using this Correct way to override Equals() and GetHashCode() and use distinct directly or do two steps
var distinctShippedItemIds = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == shipment.OrderId)
.Select(x => x.ShipmentItems.OrderItemId).Distinct()
.ToList();
List<ShippingItems> UniqueItemList=new List<ShippingItems>();
foreach(int OrderId in distinctShippedItemIds)
{
var shippedItems = _orderService.GetOrderById(shipment.OrderId).Shipments
.Where(x => x.ShippedDateUtc != null && x.OrderId == OrderId)
.Select(x => x.ShipmentItems).FirstOrDefault();
if(shippedItems !=null)
{
UniqueItemList.Add(shippedItems);
}
}
I may get you wrong but is this what you want?
SELECT * FROM [ShipmentItem]
INNER JOIN [Shipment] ON [ShipmentItem].[ShipmentId] = [Shipment].[Id]
WHERE [Shipment].[ShippedDateUtc] IS NOT NULL AND [Shipment].[OrderId] = #OrderId
So this will do the job
var shippedItems = _orderService.GetOrderById(orderId)
.Shipments
.Where(s => s.ShippedDateUtc != null)
.SelectMany(s => s.ShipmentItems)
.ToList();
Sample
Edit
If You you want to fetch the order items you have to do something like this
var orderItems = _orderService
.GetOrderById(orderId)
.Shipments
.Where(s => s.ShippedDateUtc != null)
.SelectMany(s => s.ShipmentItems)
.Select(si => _orderService.GetOrderItemById(si.OrderItemId))
.Distinct()
.ToList();
or if you want to produces less DB queries
var orderItems = _orderService
.GetOrderById(orderId)
.Shipments
.Where(s => s.ShippedDateUtc != null)
.SelectMany(s => s.ShipmentItems)
.Select(si => si.OrderItemId)
.Distinct()
.Select(id => _orderService.GetOrderItemById(id))
.ToList();
Edit 2
nopCommerce data of a order with 4 order items (quantity of 2) and 4 shipments

returning multiple column and sum using linq expression

I need to return two fields using a lambda expression. The first one is the sum of the amount field and the second one is CurrentFinancial year. Below is the code that I have written, how do I include CurrentFinancialYear?
var amount = dealingContext.vw_GetContribution
.Where(o => o.ContactID == contactId)
.Sum(o => o.Amount);
return new Contribution { Amount = amount ?? 0, CurrentFinancialYear = };
Grouping by Year should do the trick:
from entry in ledger.Entries
where entry.ContactID == contactId
&& entry.Time.Year == currentFinancialYear
group entry by entry.Time.Year
into g
select new Contribution ()
{
Amount = g.ToList ().Sum (e => e.Amount),
CurrentFinancialYear = g.Key
};
UPDATE - just return the first/default result...
(from entry in ledger.Entries
where entry.ContactID == contactId
&& entry.Time.Year == currentFinancialYear
group entry by entry.Time.Year
into g
select new Contribution ()
{
Amount = g.ToList ().Sum (e => e.Amount),
CurrentFinancialYear = g.Key
}).FirstOrDefault();
First of all use a simple select
var contribution = dealingContext.vw_GetContribution
.Where(o => o.ContactID == contactId).ToList();
It will give you a list of type vw_GetContribution
Then use groupby on this list as
var groupedContribution = contribution.GroupBy(b => b.CurrentFinancialYear).ToList();
Now you can iterate through or use this list as
foreach(var obj in groupedContribution.SelectMany(result => result).ToList())
{
var amount = obj.Amount;
var Year = obj.CurrentFinancialYear;
}
OR
In single line, you can do all the above as
var contList = context.vw_GetContribution
.Select(a => new { a.Amount, a.CurrentFinancialYear })
.GroupBy(b => b.CurrentFinancialYear)
.SelectMany(result => result).ToList();
I hope this will solve your problem.
Can you try this:
var amount = dealingContext.vw_GetContribution
.Where(o => o.ContactID == contactId)
.GroupBy(o=> new { o.CurrentFinancialYear, o.Amount})
.Select(group =>
new {
year= group.Key.CurrentFinancialYear,
sum= group.Sum(x=>x.Amount)
});

Linq group by return always values

I have this small code and I wanna return Today and count always with or not values.
DateRange dateRangeLastWeek = DateRange.LastWeek(DateTime.Now);
var caseCounts = db.Find(x => x.FinishDate.HasValue)
.Where(c => c.FinishDate.Value.Date == dateRangeLastWeek.Today.Date ||
(c.FinishDate.Value.Date >= dateRangeLastWeek.Start && c.FinishDate.Value.Date <= dateRangeLastWeek.End))
.GroupBy(c => c.FinishDate.Value.Date == dateRangeLastWeek.Today.Date ? "Today" : "Last Week")
.Select(g => new JsonChartModel
{
Name = g.Key,
Value = g.Count()
});
In this case I wanna return:
Today : 10
Last Week: 0
Not really elegant, but you could use an Union with default Values, and select the max between default and returned values.
var defaultValues = new List<JsonChartModel> {
new JsonChartModel{Name = "Today", Value = 0},
new JsonChartModel{Name = "Last Week", Value = 0}
};
var result = <YourQuery>.ToList().Union(defaultValues)
.GroupBy(m => m.Name)
.Select(g => new JsonChartModel) {
Name = g.Key,
Value = g.Max(x => x.Value)
});

Categories