What I have:
Two lists of the following model:
int SubscriptionId
int ItemId
double Usage
double EffectiveRate
string ResourceName
string UnitOfMeasure
The first contains usage data of the last month like this:
SubscriptionId ItemId Usage EffectiveRate ResourceName UnitOfMesaure
_________________________________________________________________________
1 1 2 2,75 R1 U1
1 2 3 1,50 R2 U2
The seconds contains usage data of the current month like this:
SubscriptionId ItemId Usage EffectiveRate ResourceName UnitOfMesaure
_________________________________________________________________________
1 1 5 2,75 R1 U1
1 3 2 1,50 R3 U3
What I want:
This should be merge in a list like this:
SubscriptionId ItemId UsageThis UsageLast EffRate ResName UOM
_________________________________________________________________________
1 1 5 2 2,75 R1 U1
1 2 0 3 1,50 R2 U2
1 3 2 0 1,50 R3 U3
What I have:
//data for both months available
if (resourcesThisMonth.Any() && resourcesLastMonth.Any())
{
//join both months
resources = from resourceLastMonth in resourcesLastMonth
join resourceThisMonth in resourcesThisMonth
on new { resourceLastMonth.SubscriptionId, resourceLastMonth.ItemId } equals new { resourceThisMonth.SubscriptionId, resourceThisMonth.ItemId }
select new Resource
{
SubscriptionId = resourceThisMonth.SubscriptionId,
ItemId = resourceThisMonth.ItemId,
UsageThisMonth = resourceThisMonth.Usage,
UsageLastMonth = resourceLastMonth.Usage,
EffectiveRate = resourceThisMonth.EffectiveRate,
ResourceName = resourceThisMonth.ResourceName,
UnitOfMeasure = resourceThisMonth.UnitOfMeasure
};
//resources only last month available
var resourcesOnlyLastMonth = resourcesLastMonth.Where(r => !resourcesThisMonth.Where(s => s.ItemId == r.ItemId && s.SubscriptionId == r.SubscriptionId).Any())
.Select(r => new Resource
{
SubscriptionId = r.SubscriptionId,
ItemId = r.ItemId,
UsageThisMonth = 0.0,
UsageLastMonth = r.Units,
EffectiveRate = r.EffectiveRate,
ResourceName = r.ResourceName,
UnitOfMeasure = r.UnitOfMeasure
});
//resources only this month available
var resourcesOnlyThisMonth = resourcesThisMonth.Where(r => !resourcesLastMonth.Where(s => s.ItemId == r.ItemId && s.SubscriptionId == r.SubscriptionId).Any())
.Select(r => new Resource
{
SubscriptionId = r.SubscriptionId,
ItemId = r.ItemId,
UsageThisMonth = r.Usage,
UsageLastMonth = 0.0,
EffectiveRate = r.EffectiveRate,
ResourceName = r.ResourceName,
UnitOfMeasure = r.UnitOfMeasure
});
//union data
resources = resources.Union(resourcesOnlyLastMonth);
resources = resources.Union(resourcesOnlyThisMonth);
}
//data for last month available
else if (resourcesLastMonth.Any())
{
resources = from resource in resourcesLastMonth
select new Resource
{
SubscriptionId = resource.SubscriptionId,
ItemId = resource.ItemId,
UsageThisMonth = 0.0,
UsageLastMonth = resource.Usage,
EffectiveRate = resource.EffectiveRate,
ResourceName = resource.ResourceName,
UnitOfMeasure = resource.UnitOfMeasure
};
}
//data for this month available
else if (resourcesThisMonth.Any())
{
resources = from resource in resourcesThisMonth
select new Resource
{
SubscriptionId = resource.SubscriptionId,
ItemId = resource.ItemId,
UsageThisMonth = resource.Usage,
UsageLastMonth = 0.0,
EffectiveRate = resource.EffectiveRate,
ResourceName = resource.ResourceName,
UnitOfMeasure = resource.UnitOfMeasure
};
}
//no data available
else
{
resources = new List<Resource>();
}
Problem:
This is very much code - should be less, any possible solutions failed so far
Thanks for helping!
public class ExampleClass
{
public int Id1 { get; set; }
public int Id2 { get; set; }
public int Usage { get; set; }
public int UsageThis { get; set; }
public int UsageLast { get; set; }
}
List<ExampleClass> listThisMonth = new List<ExampleClass>
{
new ExampleClass{Id1=1, Id2=1,Usage=7, UsageThis=1, UsageLast=0},
new ExampleClass{Id1=2, Id2=2,Usage=4, UsageThis=2, UsageLast=0},
new ExampleClass{Id1=3, Id2=3,Usage=1, UsageThis=3, UsageLast=0},
};
List<ExampleClass> listLastMonth = new List<ExampleClass>
{
new ExampleClass{Id1=1, Id2=1,Usage=3, UsageThis=1, UsageLast=1},
new ExampleClass{Id1=4, Id2=4,Usage=3, UsageThis=4, UsageLast=3},
new ExampleClass{Id1=2, Id2=2,Usage=1, UsageThis=8, UsageLast=6},
};
var result = listThisMonth.Select(a=>new {value=a, list=1})
.Union(listLastMonth.Select(a => new { value = a, list = 2 }))
.GroupBy(a => new { Id1 = a.value.Id1, Id2 = a.value.Id2 })
.Select(x => new ExampleClass
{
Id1 = x.Key.Id1,
Id2 = x.Key.Id2,
UsageThis = x.Any(o => o.list == 1) ? x.First(o => o.list == 1).value.Usage : 0,
UsageLast = x.Any(o => o.list == 2) ? x.First(o => o.list == 2).value.Usage : 0,
Usage = x.Sum(o=>o.value.Usage)
}).ToList();
//id1 id2 current last sum
//1 1 7 3 10
//2 2 4 1 5
//3 3 1 0 1
//4 4 0 3 3
It looks to me that what you're looking for is a full outer join. Unfortunately, it looks like LINQ doesn't have a construct for that. So, there are a few options: LINQ - Full Outer Join
For your scenario, it looks like you have some redundant code. You should be able to do the union using two outer joins to get the correct result set. For example:
// Left join the current month with the last month
var currentMonth =
from current in resourcesThisMonth
join last in resourcesLastMonth on new { current.SubscriptionId, current.ItemId } equals new { last.SubscriptionId, last.ItemId } into outer
from o in outer.DefaultIfEmpty()
select new Resource
{
SubscriptionId = current.SubscriptionId,
ItemId = current.ItemId,
UnitsThisMonth = current.Units,
UnitsLastMonth = o?.Units ?? 0, // Replace NULL with 0
EffectiveRate = current.EffectiveRate,
ResourceName = current.ResourceName,
UnitOfMeasure = current.UnitOfMeasure
};
// Reverse of the first join. Last month LEFT JOIN Current month
var lastMonth =
from last in resourcesLastMonth
join current in resourcesThisMonth on new { last.SubscriptionId, last.ItemId } equals new { current.SubscriptionId, current.ItemId } into outer
from o in outer.DefaultIfEmpty()
select new Resource
{
SubscriptionId = last.SubscriptionId,
ItemId = last.ItemId,
UnitsThisMonth = o?.Units ?? 0, // Replace NULL with 0
UnitsLastMonth = last.Units,
EffectiveRate = o?.EffectiveRate ?? last.EffectiveRate,
ResourceName = o?.ResourceName ?? last.ResourceName,
UnitOfMeasure = o?.UnitOfMeasure ?? last.UnitOfMeasure
};
// Union them together to get a full join
var resources = currentMonth.Union(lastMonth);
Related
So I have set the model here:
public class Stock
{
[Key]
//[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Guid StockId { get; set; } = Guid.NewGuid();
.....
public decimal Remaining { get; set; }
}
Also with fluent mapping:
protected override void OnModelCreating(DbModelBuilder mb)
{
base.OnModelCreating(mb);
mb.HasDefaultSchema("dbo");
mb.Entity<Stock>().Property(m => m.Remaining).HasPrecision(16, 3);
....
}
So the precision is 3 decimal points.
It is so weird when I querying with linq:
IQueryable<StockDetail> stocks = (from s in db.Stocks .... where s.Remaining > 0);
stocks.Any() --> is false;
The where s.Remaining > 0 is the problem, how to fix this?
EDIT
The actual query:
IQueryable<StockDetail> stocks = (from s in db.Stocks
join items in db.Items on s.Item equals items.ItemId
join types in db.ItemTypes on s.ItemType equals types.ItemTypeId
join colors in db.ItemColors on s.ItemColor equals colors.ItemColorId
join units in db.ItemUnits on s.Unit equals units.ItemUnitId
join buyers in db.Buyers on new { Buyer = s.Buyer } equals new { Buyer = (Guid?)buyers.BuyerId } into temp2
from buyers in temp2.DefaultIfEmpty(null)
join suppliers in db.Suppliers.DefaultIfEmpty() on new { Supplier = s.Supplier } equals new { Supplier = (Guid?)suppliers.SupplierId } into temp3
from suppliers in temp3.DefaultIfEmpty(null)
join op in db.UserProfiles on s.Operator equals op.UserId
join curr in db.Currencies on s.Currency equals curr.CurrencyId
let sales = db.Sales.Where(m => m.SId == s.Sales).FirstOrDefault()
join parent in db.Stocks on s.Parent equals parent.StockId
where s.Remaining > 0
select new StockDetail()
{
Buyer = buyers != null ? (Guid?)buyers.BuyerId : null,
BuyerName = buyers != null ? buyers.Name : null,
Supplier = suppliers.SupplierId,
SupplierName = suppliers.Name,
Code = s.Code,
Color = colors.ItemColorId,
ColorCode = colors.Code,
ColorName = colors.Color,
DateCreated = s.DateCreated,
Gramation = s.Gramation,
Item = items.ItemId,
ItemName = items.Name,
LastEdited = s.LastEdited,
Operator = op.UserId,
OperatorName = op.UserName,
PO = s.PO,
Remaining = s.Remaining,
SC = s.SC,
Qty = s.Qty,
Setting = s.ItemSetting,
StockId = s.StockId,
Type = types.ItemTypeId,
TypeName = types.Type,
Unit = units.ItemUnitId,
UnitName = units.Unit,
Lot = s.Lot,
AvgPrice = s.AvgPrice,
Currency = curr.CurrencyName,
CurrencyId = s.Currency,
Note = s.Note,
Purchase = s.Purchase,
Sales = s.Sales,
POIn = s.POIn,
FromFactory = s.FromFactory,
OutDeliveryNo = sales != null ? sales.InvoiceNo : "",
Spec = s.Spec,
DesignCode = s.DesignCode
});
This is my table structure
ID A B C D
1 null 10 5 null
2 3 5 null D2
3 8 null 2 D2
4 null 4 3 D1
5 4 6 1 D2
This is c# class and its property to store query result.
public class GrillTotals
{
public int? SumOfA {get; set;}
public int? SumOfB{get; set;}
public int? SumOfC{get; set;}
public int? CountOfD1{get; set;}
public int? CountOfD2{get; set;}
}
What I expect is:
SumOfA = 15
SumOfB = 20
SumOfC = 11
CountOfD1 = 1
CountOfD2 = 3
What I am getting is :
SumOfA = null,
SumOfB = null,
SumOfC = null,
CountOfD1 = 0,
CountOfD2 = 0
Here is a code what I have tried.
var _FinalResult = from s in dbContext.tblSchedules
group s by new
{
s.A,
s.B,
s.C,
s.D,
} into gt
select new GrillTotals
{
SumOfA = gt.Sum(g => g.A),
SumOfB = gt.Sum(g => g.B),
SumOfC = gt.Sum(g => g.C),
CountOfD1 = gt.Count(g => g.D == "D1"),
CountOfD2 = gt.Count(g => g.D == "D2"),
};
Try to correct me if I am doing something wrong or incorrectly.Any help will be appreciated.
You should not be grouping by the fields you want to calculate aggregates. When you group by them, every aggregate (Sum, Min, Max etc) will return the value itself (and Count 1 or 0 depending of the condition).
From what I see you are trying to return several aggregates with single SQL query. If that's correct, it's possible by using group by constant technique.
Just replace
group s by new
{
s.A,
s.B,
s.C,
s.D,
} into gt
with
group s by 1 // any constant
into gt
Try this:
var _FinalResult = from s in dbContext.tblSchedules
group s by new
{
s.A,
s.B,
s.C,
s.D,
} into gt
select new GrillTotals
{
SumOfA = gt.Sum(g => g.A ?? 0),
SumOfB = gt.Sum(g => g.B ?? 0),
SumOfC = gt.Sum(g => g.C ?? 0),
CountOfD1 = gt.Count(g => g.D == "D1"),
CountOfD2 = gt.Count(g => g.D == "D2"),
};
I'm running out of ideas on how to uncompress an array (request array A[] to response array B[])
Here are my definitions
A is a request class.
class A
{
public string Date { get; set; }
public decimal Price { get; set; }
}
Below is my array of requests of class A with its initalization.
var request = new A[]
{
new A { Date = "14-04-2016", Price = 100 },
new A { Date = "15-04-2016", Price = 100 },
new A { Date = "16-04-2016", Price = 0 },
new A { Date = "17-04-2016", Price = 100 },
new A { Date = "18-04-2016", Price = 100 }
};
B is a respond class.
class B
{
public string Start { get; set; }
public string End { get; set; }
public decimal Price { get; set; }
}
The above requests needs to be converted to an array of B. Something like this
var response = new B[]
{
new B { Start = "14-04-2016", End = "16-04-2016", Price = 100 },
new B { Start = "16-04-2016", End = "17-04-2016", Price = 0 },
new B { Start = "17-04-2016", End = "18-04-2016", Price = 100 }
};
The response is grouped based on the Price and order by date. Its more of like, I need to uncompress the request array A[] into response array B[].
How can I achieve this?
You could do this using GroupBy linq extension, following query returns List<B>objects.
var results = request.Select(s=>
new
{
Price = s.Price,
Date = DateTime.ParseExact(s.Date, "dd-MM-yyyy", null) // convert to Date.
})
.GroupBy(g=>g.Price)
.Select(s=>
new B()
{
Start = s.Min(c=>c.Date).ToString("dd-MM-yyyy"),
End = s.Max(c=>c.Date).ToString("dd-MM-yyyy"),
Price = s.Key
})
.ToList() ;
Update :
As per comments, you don't really require grouping on price. What you need is grouping adjacent items whose price is matching .
We could achieve this with slight modification to above Linq query.
int grp = 0;
decimal prevprice=response.First().Price;
var results = request.Select((s, i)=>
{
grp = s.Price == prevprice? grp : ++grp;
prevprice = s.Price;
return new
{
grp,
Price = s.Price,
Date = DateTime.ParseExact(s.Date, "dd-MM-yyyy", null)
};
})
.GroupBy(g=>g.grp)
.Select(s=>
new B()
{
Start = s.Min(c=>c.Date).ToString("dd-MM-yyyy"),
End = s.Max(c=>c.Date).ToString("dd-MM-yyyy"),
Price = s.First().Price
});
Output
14-04-2016,15-04-2016 ,100
16-04-2016,16-04-2016 ,0
17-04-2016,18-04-2016 ,100
Working Example
Pseudocode (assumes request is ordered by date - if not you can sort it easily):
int lastPrice = -1;
//count the distinct price ranges
int responseSize = 0;
foreach (A requestObj in request) {
if (requestObj.price != lastPrice) {
responseSize++;
lastPrice = requestObj.price;
}
}
//set the initial element
B[] response = new B[responseSize];
response[0].start = request[0].date;
response[0].price = request[0].price;
int responseindex = 0;
//parse the result
foreach (A requestObj in request) {
if (requestObj.price != response[responseindex].price) {
response[responseindex].end = requestObj.date;
responseIndex++;
response[responseindex].price = requestObj.price;
response[responseindex].start= requestObj.date;
}
}
//set the end date of the final object
response[response.length - 1].end = request[request.length - 1].date;
This can also be done with the following:
var response = from reqItem in request
group reqItem by reqItem.Price into g
select new B()
{
Start = g.Min(m => DateTime.Parse(m.Date)).ToString("dd-MM-yyyy"),
End = g.Max(m => DateTime.Parse(m.Date)).ToString("dd-MM-yyyy"),
Price = g.Key
};
I have the following type:
public class Parts
{
public string PartNo { get; set; }
public decimal Price { get; set; }
}
what I would like to do is to compare the price or each part with the cheapest one and display the difference in percentage.
This is what I have tried so far and it works:
var part1 = new Part {PartNo = "part1", Price = 10};
var part2 = new Part {PartNo = "part1", Price = 8};
var part3 = new Part {PartNo = "part1", Price = 12};
var parts = new List<Part> {part1, part2, part3};
var list = from p in parts
orderby p.Price ascending
select p;
var sb = new StringBuilder();
var counter = 0;
decimal firstPrice=0;
foreach (Part p in list)
{
if (counter == 0)
{
firstPrice = p.Price;
}
sb.Append(p.PartNo + ": " + p.Price + "," + ((p.Price/firstPrice)-1)*100 + Environment.NewLine);
counter++;
}
Console.WriteLine(sb.ToString(), "Parts List");
This outputs the following:
part1: 8, 0
part1: 10, 25.00
part1: 12, 50.0
This shows the price increase for each each part, and that is what I am trying to achieve but I was wondering is there a better way of calculating the price difference in percentage (e.g. with a LINQ query) or in any other way.
Thanks
I would calculate the difference as first step.
var cheapestPrice = parts.Min(p => p.Price);
var list = parts.Select(p => new {
Part = p,
DiffPercentage = ((p.Price - cheapestPrice) / cheapestPrice) * 100
});
foreach (var p in list)
Console.WriteLine("{0}: {1},{2}%", p.Part.PartNo, p.Part.Price, p.DiffPercentage);
// list defined as sorted by price ascending as per the code
var list = parts.OrderBy(p => p.Price); // less verbose way of saying the same
var firstPrice = list.First().Price;
var differences = list.Skip(1).Select(s => new {Part = s, PercentageDiff = (s.Price/firstPrice - 1)*100});
The .Skip(1) is optional. You may not want to compare cheapest price to itself.
Tim beat me to it, but use the select to create your string
void Main()
{
var part1 = new Part {PartNo = "part1", Price = 10};
var part2 = new Part {PartNo = "part1", Price = 8};
var part3 = new Part {PartNo = "part1", Price = 12};
var parts = new List<Part> {part1, part2, part3};
var lowest = parts.Min(p => p.Price );
var result = parts.Select (p => string.Format("Part #:{0} {1} -> {2}", p.PartNo, p.Price, ((p.Price/lowest)-1)*100 ));
result.ToList()
.ForEach(rs => Console.WriteLine (rs));
/*
Part #:part1 10 -> 25.00
Part #:part1 8 -> 0
Part #:part1 12 -> 50.0
*/
}
// Define other methods and classes here
public class Part
{
public string PartNo { get; set; }
public decimal Price { get; set; }
}
I'm still learning LINQ and have a task where I need to group Booking objects by four properties and then by weekly intervals depending on the input timerange.
public class Booking
{
public string Group { get; set; }
public BookingType Type { get; set; }
public BookingStatus Status { get; set; }
public decimal Price { get; set; }
public bool Notification { get; set; }
public DateTime Date { get; set; }
}
Let's say we have the following Bookings:
IList<Booking> Bookings = new List<Booking>
{
new Booking{Group = "Group1", Type = BookingType.Online, Status = BookingStatus.New, Price = 150, Date = new DateTime(2012,06,01)},
new Booking{Group = "Group1", Type = BookingType.Online, Status = BookingStatus.New, Price = 100, Date = new DateTime(2012,06,02)},
new Booking{Group = "Group1", Type = BookingType.Online, Status = BookingStatus.New, Price = 200, Date = new DateTime(2012,06,03)},
new Booking{Group = "Group2", Type = BookingType.Phone, Status = BookingStatus.Accepted, Price = 80, Date = new DateTime(2012,06,10)},
new Booking{Group = "Group2", Type = BookingType.Phone, Status = BookingStatus.Accepted, Price = 110, Date = new DateTime(2012,06,12)},
new Booking{Group = "Group3", Type = BookingType.Store, Status = BookingStatus.Accepted, Price = 225, Date = new DateTime(2012,06,20)},
new Booking{Group = "Group3", Type = BookingType.Store, Status = BookingStatus.Invoiced, Price = 300, Date = new DateTime(2012,06,21)},
new Booking{Group = "Group3", Type = BookingType.Store, Status = BookingStatus.Invoiced, Price = 140, Date = new DateTime(2012,06,22)},
};
That would result in the following lines on the final printout:
Week 22 Week 23 Week 24 Week 25 Week 26
May28-Jun3 Jun4-10 Jun11-17 Jun18-24 Jun25-Jul1
Group Type Status Cnt. Price Cnt. Price Cnt. Price Cnt. Price Cnt. Price
----------------------------------------------------------------------------------------------
Group1 Online New 3 450 0 0 0 0 0 0 0 0
Group2 Phone Accepted 0 0 1 80 1 110 0 0 0 0
Group3 Store Accepted 0 0 0 0 0 0 1 225 0 0
Group3 Store Invoiced 0 0 0 0 0 0 2 440 0 0
I have created 2 additional classes to represent a line with the possibility to include any number of weeks:
public class GroupedLine
{
public string Group { get; set; }
public BookingType Type { get; set; }
public BookingStatus Status { get; set; }
public List<WeeklyStat> WeeklyStats { get; set; }
}
public class WeeklyStat
{
public DateTime WeekStart { get; set; }
public decimal Sum { get; set; }
public int Count { get; set; }
}
If I have the following time period:
var DateFrom = new DateTime(2012, 05, 28);
var DateTo = new DateTime(2012, 7, 01);
Firstly, I need to identify what weeks are necessary in the statistics: in this case week 22-26.
For that I have the following code:
var DateFrom = new DateTime(2012, 05, 28);
var DateTo = new DateTime(2012, 7, 01);
var firstWeek = GetFirstDateOfWeek(DateFrom, DayOfWeek.Monday);
IList<DateTime> weeks = new List<DateTime> { firstWeek };
while(weeks.OrderByDescending(w => w).FirstOrDefault().AddDays(7) <= DateTo)
{
weeks.Add(weeks.OrderByDescending(w => w).FirstOrDefault().AddDays(7));
}
And now, I'd need some LINQ magic to do the grouping both by the 4 properties and the aggregation (count of bookings and sum of prices) for the weeks.
I can attach code sample of the LINQ I got so far tomorrow, as I don't have access to it now.
Sorry for the long post, hope it's clear what I mean.
Edit: 2012-11-07
I have to modify the question a bit, so that the grouped weeks only include those weeks, that actually have data.
Updated example output:
May28-Jun3 Jun4-10 Jun18-24
Group Type Status Cnt. Price Cnt. Price Cnt. Price
---------------------------------------------------------------
Group1 Online New 3 450 0 0 0 0
Group2 Phone Accepted 0 0 1 80 0 0
Group3 Store Accepted 0 0 0 0 1 225
Group3 Store Invoiced 0 0 0 0 2 440
In this case there were no Bookings in period Jun 11-17 and Jun25-Jul1 so they are omitted from the results.
This query will get all data
var query = from b in Bookings
where b.Date >= dateFrom && b.Date <= dateTo
group b by new { b.Group, b.Type, b.Status } into g
select new GroupedLine()
{
Group = g.Key.Group,
Type = g.Key.Type,
Status = g.Key.Status,
WeeklyStats = (from b in g
let startOfWeek = GetFirstDateOfWeek(b.Date)
group b by startOfWeek into weekGroup
orderby weekGroup.Key
select new WeeklyStat()
{
WeekStart = weekGroup.Key,
Count = weekGroup.Count(),
Sum = weekGroup.Sum(x => x.Price)
}).ToList()
};
I leave UI output to you :)
This will also return WeekStats for all weeks (with 0 values, if we do not have booking groups on some week):
// sequence contains start dates of all weeks
var weeks = Bookings.Select(b => GetFirstDateOfWeek(b.Date))
.Distinct().OrderBy(date => date);
var query = from b in Bookings
group b by new { b.Group, b.Type, b.Status } into bookingGroup
select new GroupedLine()
{
Group = bookingGroup.Key.Group,
Type = bookingGroup.Key.Type,
Status = bookingGroup.Key.Status,
WeeklyStats = (from w in weeks
join bg in bookingGroup
on w equals GetFirstDateOfWeek(bg.Date) into weekGroup
orderby w
select new WeeklyStat()
{
WeekStart = w,
Count = weekGroup.Count(),
Sum = weekGroup.Sum(b => b.Price)
}).ToList()
};
Keep in mind, that if you need date filter (from, to), then you need to apply it both to weeks query and bookings query.
var query = Bookings.GroupBy(book => new GroupedLine()
{
Group = book.Group,
Type = book.Type,
Status = book.Status
})
.Select(group => new
{
Line = group.Key,
Dates = group.GroupBy(book => GetWeekOfYear(book.Date))
.Select(innerGroup => new
{
Week = innerGroup.Key,
Count = innerGroup.Count(),
TotalPrice = innerGroup.Sum(book => book.Price)
})
});
public static int GetWeekOfYear(DateTime date)
{
return date.DayOfYear % 7;
}
This variant generates "empty" spots for weeks with no data:
// I group by week number, it seemed clearer to me , but you can change it
var firstWeek = cal.GetWeekOfYear(DateFrom, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);
var lastWeek = cal.GetWeekOfYear(DateTo, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);
var q = from b in Bookings
group b by new { b.Group, b.Type, b.Status }
into g
select new
{
Group = g.Key,
Weeks =
from x in g
select new
{
Week = cal.GetWeekOfYear(x.Date,
dfi.CalendarWeekRule,
dfi.FirstDayOfWeek),
Item = x
}
} into gw
from w in Enumerable.Range(firstWeek, lastWeek-firstWeek+1)
select new
{
gw.Group,
Week = w,
Item = from we in gw.Weeks
where we.Week == w
group we by we.Item.Group into p
select new
{
p.Key,
Sum = p.Sum (x => x.Item.Price),
Count = p.Count()
}
};