How do I create groups and subgroup1 and subgroup2 Use linq - c#

How do I create groups and subgroup1 and subgroup2 Use linq.
Example of this picture
I want to create json.
Example of this picture.
I tried to do this but there was a problem.
The items are repeated within one subgroup2.
var list = result
.GroupBy(x => new { x.GroupId, x.GroupName })
.Select(g => new
{
ID = g.Key.GroupId,
Name = g.Key.GroupName,
SubGroup1 = g.GroupBy(x => new { x.SubGroupID1, x.SubGroupName1 })
.Select(cg => new
{
ID = cg.Key.SubGroupID1,
Name = cg.Key.SubGroupName1,
SubGroup2 = g.GroupBy(x => new { x.SubGroupID2, x.SubGroupName2 })
.Select(ii => new
{
ID = ii.Key.SubGroupID2,
Name = ii.Key.SubGroupName2,
item = ii.GroupBy(x => new { x.Stock_Id, x.Stock_Name, x.Prices, x.ScreenNumber })
.Select(oo => new
{
Stock_Id = oo.Key.Stock_Id,
Stock_Name = oo.Key.Stock_Name,
Prices = oo.Key.Prices,
ScreenNumber = oo.Key.ScreenNumber
}).OrderBy(Or => Or.Stock_Id)
.ToList()
}).OrderBy(Or => Or.ID)
.ToList()
}).OrderBy(Or => Or.ID)
.ToList()
}).OrderBy(Or => Or.ID)
.ToList();

Your query could be a lot cleaner if you grouped the groups up front, then project out to your desired results.
var query =
from x in data
group new { x.StockId, x.StockName, x.Prices, x.ScreenNumber }
by new { x.GroupId, x.GroupName, x.SubGroupId1, x.SubGroupName1, x.SubGroupId2, x.SubGroupName2 }
into g
group g
by new { g.Key.GroupId, g.Key.GroupName, g.Key.SubGroupId1, g.Key.SubGroupName1 }
into g2
group g2
by new { g2.Key.GroupId, g2.Key.GroupName }
into g1
select new
{
Id = g1.Key.GroupId,
Name = g1.Key.GroupName,
SubGroup1 = g1.Select(g2 => new
{
Id = g2.Key.SubGroupId1,
Name = g2.Key.SubGroupName1,
SubGroup2 = g2.Select(g => new
{
Id = g.Key.SubGroupId2,
Name = g.Key.SubGroupName2,
Items = g.Select(x => new
{
x.StockId,
x.StockName,
x.Prices,
x.ScreenNumber,
}),
}),
}),
};
The idea is to start off with the most specific grouping first, then one-by-one group the groups by the next layer, and so on.

SubGroup2 = g.GroupBy(x => new { x.SubGroupID2, x.SubGroupName2 })
You are grouping g instead of cg.
I suggest structuring your code a bit, which would help avoiding this kind of mistake.

Related

How to Add Rownum to GroupBy Linq

I have a complex LINQ Query to extract Top students in my university. Here is the query :
var query = Db.Students.AsNoTracking().Where(...).AsQueryable();
var resultgroup = query.GroupBy(st => new
{
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
}, (key, g) => new
{
CourseStudyId = key.CourseStudyId,
EntranceTermId = key.EntranceTermId,
StudyingModeId = key.StudyingModeId,
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm).Take(topStudentNumber)
}).SelectMany(q => q.list).AsQueryable();
This Query give me top n students based on 4 parameters and on their TotalAverageTillTerm.
Now I want to add rownum for each group to simulate Total rank, for example Output is :
Now I want to Add TotalRank as rownumber like Sql. In the picture X1=1,X2=2,X3=3 and Y1=1,Y2=2,Y3=3
If I want to reduce problem. I only work on one group. Code Like this :
resultgroup = query.GroupBy(st => new
{
st.Student.StudyLevelId
}, st => st, (key, g) => new
{
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
}).SelectMany(q => q.list).AsQueryable();
list was a List of student but I see no sign of student having a rank property so I wrapped it into a annonimous type with rank.
var query = Db.Students.AsNoTracking().Where(...).AsEnumerable();
var resultgroup = query.GroupBy(st => new {
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
})
.SelectMany( g =>
g.OrderByDescending(x =>x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
.Select((x,i) => new {
CourseStudyId = g.Key.CourseStudyId,
EntranceTermId = g.Key.EntranceTermId,
StudyingModeId = g.Key.StudyingModeId,
StudyLevelId = g.Key.StudyLevelId,
Rank = i+1
//studentPorperty = x.Prop1,
})
)
.AsQueryable();
Do you mean :
var query = Db.Students.AsNoTracking().Where(...).AsQueryable();
var resultgroup = query.GroupBy(st => new
{
st.Student.CourseStudyId,
st.Student.EntranceTermId,
st.Student.StudyingModeId,
st.Student.StudyLevelId
}, (key, g) => new
{
CourseStudyId = key.CourseStudyId,
EntranceTermId = key.EntranceTermId,
StudyingModeId = key.StudyingModeId,
StudyLevelId = key.StudyLevelId,
list = g.OrderByDescending(x =>
x.StudentTermSummary.TotalAverageTillTerm)
.Take(topStudentNumber)
.Select((x, i) => new { Item = x, TotalRank = i /* item number inside group */}),
StudentsInGroupCount = g.Count() // count group this items
}).SelectMany(q => q).AsQueryable();
To see the results :
foreach (var item in resultgroup.ToList())
{
item.list.ForEach(s => Console.WriteLine(s.TotalRank));
}

Group and Map a List with a gRPC List with LINQ

I'm working with a gRPC service and I have a list of Hotels where It could happen that there is the same hotel repeated but with different rooms or quotes, I would like to group those Hotels with same id and add the different Rooms inside it. I'm trying to reach it with Linq but getting this error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'Google.Protobuf.Collections.RepeatedField'
This is the block of code.
private Grpc.CheckRateResponse BuildGrpcResponseResult(List<Hotels> hotelsList)
{
var listResult = hotelsList.GroupBy(h => h.Id)
.Select(g => new Grpc.Hotels
{
Id = g.Key,
Name = g.Where(c => c.Id == g.Key).FirstOrDefault().Name,
CategoryCode = g.Where(c => c.Id == g.Key).FirstOrDefault().CategoryCode
Rooms = g.SelectMany(h => h.Rooms).GroupBy(r => r.RoomId).Select(x => new Grpc.Room
{
RoomId = x.Key,
RoomName = x.Where(l => l.RoomId == x.Key).FirstOrDefault().RoomName,
Rates = x.SelectMany(r => r.Rates).Select(c => new Grpc.Rate
{
RateKey = c.RateKey,
RateType = c.RateType,
BoardCode = c.BoardCode
})
})
});
Grpc.CheckRateResponse result = new Grpc.CheckRateResponse();
result.Hotels.Add(listResult);
return result;
}

Linq : Use variable in a GroupBy

I have help this LINQ request :
My c#:
var stats = UoW.Repository
.Get(echangeFilter)
.GroupBy(a => new
{
Id = MyBLL.IsInComing(a.idSens) ? MyBLL.FindByNoContactModel(a.change.idTo.GetValueOrDefault()).id : MyBLL.FindByNoContactModel(a.change.idFrom.GetValueOrDefault()).id,
Tri = MyBLL.IsInComing(a.idSens) ? MyBLL.FindByNoContactModel(a.change.idTo.GetValueOrDefault()).Tri: MyBLL.FindByNoContactModel(a.change.idFrom.GetValueOrDefault()).Tri,
SensAppel = a.echange_sens.nom
})
.Select(group => new
{
group.Key.Id,
group.Key.Tri,
group.Key.SensAppel,
Count = group.Count(),
})
.OrderBy(g => g.Tri)
.ToList();
It works but I want to remove this horrible duplication (Id and Tri) in this groupBy, how can I deal with that ? In the concept, I want to use a variable to call this ternary once
If you use the query syntax, you can declare variables.
This is how your query would look like in query syntax (please name your variables properly. I don't know what you are actually doing so I can't name them...):
var query = from a in UoW.Repository
.Get(echangeFilter)
group a by new
{
Id = MyBLL.IsInComing(a.idSens) ? MyBLL.FindByNoContactModel(a.change.idTo.GetValueOrDefault()).id : MyBLL.FindByNoContactModel(a.change.idFrom.GetValueOrDefault()).id,
Tri = MyBLL.IsInComing(a.idSens) ? MyBLL.FindByNoContactModel(a.change.idTo.GetValueOrDefault()).Tri: MyBLL.FindByNoContactModel(a.change.idFrom.GetValueOrDefault()).Tri,
SensAppel = a.echange_sens.nom
} into g
let b = new
{
group.Key.Id,
group.Key.Tri,
Count = group.Count(),
}
orderby g.Tri
select g;
var stats = query.ToList();
Now we can introduce a let:
var query = from a in UoW.Repository
.Get(echangeFilter)
let x = MyBLL.IsInComing(a.idSens) ? MyBLL.FindByNoContactModel(a.change.idTo.GetValueOrDefault()) : MyBLL.FindByNoContactModel(a.change.idFrom.GetValueOrDefault())
group a by new
{
Id = x.id,
Tri = x.Tri,
SensAppel = a.echange_sens.nom
} into g
let b = new
{
g.Key.Id,
g.Key.Tri,
Count = g.Count(),
}
orderby g.Tri
select g;
var stats = query.ToList();
Is this what you're looking for?
var stats = UoW.Repository
.GroupBy(a => {
var repo = MyBLL.IsInComing(a.idSens) ? MyBLL.FindByNoContactModel(a.change.idTo.GetValueOrDefault()) : MyBLL.FindByNoContactModel(a.change.idFrom.GetValueOrDefault())
return new
{
Id = repo.id,
Tri = repo.Tri,
SensAppel = a.echange_sens.nom
}
})
.Select(group => new
{
group.Key.Id,
group.Key.Tri,
Count = group.Count(),
})
.OrderBy(g => g.Tri)
.ToList();
This works by first returning whatever model FindByNoContactModel returns per record and SensAppel, so you only have to call the IsIncoming and FindByNoContactModel once per row. Depending on how many rows you expect to be returned, you may find very quickly that you are better off pushing these lookups elsewhere, or letting the database do it.
var stats = UoW.Repository
.Get(echangeFilter)
.Select(a=> new {
Model = MyBLL.IsIncoming(a.idSens)
? MyBLL.FindByNoContactModel(a.change.idTo.GetValueOrDefault())
: MyBLL.FindByNoContactModel(a.change.idFrom.GetValueOrDefault()),
SensAppel = a.echange_sens.nom
})
.GroupBy(a => new
{
Id = a.Model.id,
Tri = a.Model.Tri,
SensAppel = a.SensAppel
})
.Select(group => new
{
group.Key.Id,
group.Key.Tri,
group.Key.SensAppel,
Count = group.Count(),
})
.OrderBy(g => g.Tri)
.ToList();
You can combine the sub-expressions by pulling out the common elements and then using an intermediate anonymous class to hold them (I am not sure how EF 6 will handle this (what does Get return?) as I don't know what MyBLL is):
var stats = UoW.Repository
.Get(echangeFilter)
.Select(a => new {
a.echange_sens.nom,
fncm = MyBLL.FindByNoContactModel(
(MyBLL.IsInComing(a.idSens)
? a.change.idTo
: a.change.idFrom)
.GetValueOrDefault())
})
.GroupBy(nf => new {
Id = nf.fncm.id,
Tri = nf.fncm.Tri,
SensAppel = nf.nom
})
.Select(group => new {
group.Key.Id,
group.Key.Tri,
Count = group.Count(),
})
.OrderBy(g => g.Tri)
.ToList();

Optimize linq query by storing value in select

I have problem with linq query. In Select I am getting the same item twice which makes code execution much longer than I can afford. Is there any way to store x.OrderByDescending(z => z.Date).FirstOrDefault() item inside Select query?
Execution time: 180 ms
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new HistoryReportItem
{
AccountNo = x.FirstOrDefault().AccountNo,
BankName = x.FirstOrDefault().BankName,
IsActive = x.FirstOrDefault().IncludeInCheck,
})
.ToList();
Execution time: 1200 ms
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new HistoryReportItem
{
AccountNo = x.FirstOrDefault().AccountNo,
BankName = x.FirstOrDefault().BankName,
IsActive = x.FirstOrDefault().IncludeInCheck,
LastDate = x.OrderByDescending(z => z.Date).FirstOrDefault().Date,
})
.ToList();
Execution time: 2400 ms
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new HistoryReportItem
{
AccountNo = x.FirstOrDefault().AccountNo,
BankName = x.FirstOrDefault().BankName,
IsActive = x.FirstOrDefault().IncludeInCheck,
LastDate = x.OrderByDescending(z => z.Date).FirstOrDefault().Date,
DataItemsCount = x.OrderByDescending(z => z.Date).FirstOrDefault().CountItemsSend
})
.ToList();
You can try doing the select in two steps:
var groups = dataContext.History
.GroupBy(a => new { a.BankName, a.AccountNo })
.Select(x => new
{
first = x.FirstOrDefault();
lastDate = x.OrderByDescending(z => z.Date).FirstOrDefault();
}
.Select(x => new HistoryReportItem
{
AccountNo = x.first.AccountNo,
BankName = x.first.BankName,
IsActive = x.first.IncludeInCheck,
LastDate = x.lastDate.Date,
DataItemsCount = x.lastDate.CountItemsSend
})
.ToList();
If this fails, it might be because the engine can't convert it completely to SQL, and you can try adding an AsEnumerable() between the two Selects.

Linq group by with parent object

How do I group so that I don't loose the parent identifier.
I have the following
var grouped = mymodel.GroupBy(l => new { l.AddressId })
.Select(g => new
{
AddressId = g.Key.AddressId,
Quotes = g.SelectMany(x => x.Quotes).ToList(),
}).ToList();
this returns
{ AddressId1, [Quote1, Quote2, Quote3...]}
{ AddressId2, [Quote12, Quote5, Quote8...]}
Now I would like to group these by Quote.Code and Quote.Currency, So that Each address has 1 Object-Quote (that is if all 4 quotes belonging to the address have the same Code and Currency). I would like the sum of Currency in that object.
This works, but I can't get how to add Address to this result:
var test = grouped.SelectMany(y => y.Quotes).GroupBy(x => new { x.Code, x.Currency }).Select(g => new
{
test = g.Key.ToString()
});}
this gives compile error, whenever i try to add AddressId to result:
var test1 = grouped.SelectMany(y => y.Quotes, (parent, child) => new { parent.AddressId, child }).GroupBy(x => new { x.Provider, x.Code, x.Currency, x.OriginalCurrency }).Select(g => new
{
test = g.Key.ToString(),
Sum = g.Sum(x => x.Price)
});
compiler error as well:
var test1 = grouped.Select(x => new { x.AddressId, x.Quotes.GroupBy(y => new { y.Provider, y.Code, y.Currency, y.OriginalCurrency }).Select(g => new
{
addr = x.AddressId,
test = g.Key.ToString(),
Sum = g.Sum(q => q.Price)
};
I would do that this way:
var grouped = mymodel.GroupBy(l => new { l.AddressId })
.Select(g => new
{
AddressId = g.Key.AddressId,
QuotesByCode = g.SelectMany(x => x.Quotes)
.GroupBy(x=>x.Code)
.Select(grp=>new
{
Code = grp.Key.Code,
SumOfCurrency=grp.Sum(z=>z.Currency)
}).ToList(),
}).ToList();

Categories