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();
Related
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));
}
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;
}
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.
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.
var residenceRep =
ctx.ShiftEmployees
.Include(s => s.UserData.NAME)
.Include(s => s.ResidenceShift.shiftName)
.Join(ctx.calc,
sh => new { sh.empNum, sh.dayDate },
o => new { empNum = o.emp_num, dayDate = o.trans_date },
(sh, o) => new { sh, o })
.Where(s => s.sh.recordId == recordId && s.o.day_flag.Contains("R1"))
.OrderBy(r => r.sh.dayDate)
.Select(r => new
{
dayDate = r.sh.dayDate,
empNum = r.sh.empNum,
empName = r.sh.UserData.NAME,
shiftId = r.sh.shiftId,
shiftName = r.sh.ResidenceShift.shiftName,
recordId,
dayState = r.o.day_desc.Split('[', ']')[1]
}).ToList();
I get an exception :
The LINQ expression node type 'ArrayIndex' is not supported in LINQ to
Entities
How i could find an alternative to Split('[', ']')[1] in this query
You must commit the query and do the split after loading the data:
var residenceRep =
ctx.ShiftEmployees
.Include(s => s.UserData.NAME)
.Include(s => s.ResidenceShift.shiftName)
.Join(ctx.calc,
sh => new { sh.empNum, sh.dayDate },
o => new { empNum = o.emp_num, dayDate = o.trans_date },
(sh, o) => new { sh, o })
.Where(s => s.sh.recordId == recordId && s.o.day_flag.Contains("R1"))
.OrderBy(r => r.sh.dayDate)
.Select(r => new
{
dayDate = r.sh.dayDate,
empNum = r.sh.empNum,
empName = r.sh.UserData.NAME,
shiftId = r.sh.shiftId,
shiftName = r.sh.ResidenceShift.shiftName,
recordId = r.sh.recordId,
dayState = r.o.day_desc,
})
.ToList()//Here we commit the query and load data
.Select(x=> {
var parts = x.dayState.Split('[', ']');
return new {
x.dayDate,
x.empNum,
x.empName,
x.shiftId,
x.shiftName,
x.recordId,
dayState = parts.Length > 1 ?parts[1]:"",
};
})
.ToList();
I had this Issue and the approach that I've chose was that get all element I wanted and save them into a List and then filter the actual data on that list.
I know this is not the best answer but it worked for me.