I have a list of type Cars like below List<Car> cars:
ID
Colour
Make
Model
Km
1
Red
BMW
M3
1000
2
Red
BMW
318i
1000
3
Black
Mercedes
C200
1000
4
Black
Mercedes
E200
1000
5
White
Mercedes
CLA200
1000
6
White
Mercedes
E200
1000
7
White
Mercedes
E200
2000
8
White
Mercedes
E200
3000
Now what I want to do is, using LINQ (or maybe another way if not possible with LINQ) I want to group them by Colour, Make, Model and then add another column and assign to list of another type (<CarDetail>) which will give the weighted average of the line in the group by.
As an example, on last 3 Cars, the new column will show (Total Km for that group = 1000+2000+3000 = 6000).
6 => 1000/6000
7 => 2000/6000
8 => 3000/6000
What I have tried so far is :
List<CarDetail> carDetails =
cars.
GroupBy(x => new
{
x.Colour,
x.Make,
x.Model
}).
Select(h => new CarDetail
{
Colour= h.Key.Colour,
Make= h.Key.Make,
Model= h.Key.Model,
WeightedKm = x.Km / h.Sum(x => x.Km )
}).ToList();
h.Sum(x => x.Km ) will bring the sum of Km.s but trying to use "x.Km" surely doesn't work.
Can anyone help on how to get this list please?
A grouping is conceptually a list of lists. It seems you want to unpack this to a simple list, using an aggregation of the sub list along the way. The process for turning a list of lists into a list is a SelectMany
var carDetails = cars
.GroupBy(c => new
{
c.Colour,
c.Make,
c.Model
})
.SelectMany(g => g, (subList, aCarInTheSublist) => new CarDetail
{
Colour= aCarInTheSublist.Colour,
Make= aCarInTheSublist.Make,
Model= aCarInTheSublist.Model,
WeightedKm = (double)aCarInTheSublist.Km / subList.Sum(aCar => aCar.Km ) //cast to double because int math 1000/3000 = 0
}).ToList();
Attacking it with a Select will run into trouble because what you're selecting is the group i.e. the outer list. Taking this simpler example:
A, 1
A, 2
A, 3
B, 1
If you group by the letter, your output only has 2 rows:
A -> {A,1} {A,2} {A,3}
B -> {B,1}
so you'll struggle to get back to the 4 rows you want, because the Select is only run twice.
SelectMany will visit each entry in the group (i.e. the two groups A and B, the A group having 3 sub-entries) and visit each one of the sub entries, so for the A group (a single group with 3 members, A1, A2 and A3) it will result in projecting 3 elements out. Crucially because, whilst it visits each of A1, A2 and A3, access to the group A as a whole is still available so you can sum it all the KMs in it
It's perhaps not very efficient, because we're summing up the group repeatedly but LINQ doesn't always win efficiency contests :)
You could consider trading CPU time in summing for memory in remembering the sum by putting the sums in a dictionary:
var d = cars
.GroupBy(c => new
{
c.Colour,
c.Make,
c.Model
})
.ToDictionary(g=>g.Key, g=>g.Sum(c=>c.Km));
var result = cars
.Select(c => new CarDetail
{
Colour= c.Colour,
Make= c.Make,
Model= c.Model,
WeightedKm = (double)c.Km / d[new{c.Colour,c.Make,c.Model}]
}).ToList();
Side note, when using LINQ try and keep aliases representative of what the list entry is. cars is a list of car, so Select(c => to help keep straight that it's a car.
A GroupBy(c=>...).Select(g=> reminds you that you're grouping up cars and then Select is operating on a grouping. Each item within the grouping is a Car, so you might like Select(gc=> for "group of cars", and Select(gc=> gc.Select(c=> the second select is operating on an enumerable of Car, so reverting to c helps clarify that
Please consider these two tables in my database:
Header:
Id Name
-------------------------------
1 London
2 Berlin
3 Paris
and Details:
Id HeaderId Amount YearMonth
--------------------------------------------------------------------
1 1 1000 2010-01
2 1 2000 2010-05
3 2 3000 2015-04
4 2 2700 2017-12
5 2 4500 2016-10
6 2 7000 2011-09
7 1 3000 2009-05
I want Header records with related Last Details record. For example:
HeaderId HeaderName Amount
----------------------------------------------------
1 London 2000
2 Berlin 2700
3 Paris Null
I wrote this query for Inner Join version (But I want Outer Join version):
from h in Header
join d in Details
on h.Id equals d.HeaderId
select new
{
HeaderId = h.Id,
HeaderName = h.Name,
Amount = (Details.Where(k=>k.HeaderId == h.Id).OrderBy(m=>m.YearMonth).LastOrDefault() == null ? null : Details.Where(k=>k.HeaderId == h.Id).OrderBy(m=>m.YearMonth).LastOrDefault().Amount,
}
and I got this error:
System.NotSupportedException: LINQ to Entities does not recognize the method 'Details.LastOrDefault()Details' method, and this method cannot be translated into a store expression.
How can I get above result?
thanks
This query should return desired result:
from h in Header
from d in Details.Where(d => d.HeaderId == h.Id)
.OrderByDescending(d => d.YearMonth)
.Take(1)
.DefaultIfEmpty()
select new
{
HeaderId = h.Id,
HeaderName = h.Name,
Amount = d.Amount
}
You should change your code as :
Amount = Details.Where(k=>k.HeaderId == h.Id).OrderByDescending(m => m.YearMonth).FirstOrDefault(o=>o.Amount);
Table 1
Id Name
1 Tank 1
2 Tank 2
3 Tank 3
4 Tank 4
Table 2
Id Name
1 Tank 1
2 Tank 2
Result List it should return
Id Name
1 Tank 1
2 Tank 2
3 Tank 3
4 Tank 4
I need to form a linq query using .FindByExp only. Like below i need to use joins and arrive at above list Result.
var tankLst = context.Tank.FindByExp(a => a.Id== S.Id).ToList();
var distinctNames = context.Table1.FindAll().Select(x=>x.Name).Distinct().ToArray();
var tab1 = context.Table1.FindAll().ToList();
var tab2 = context.Table2.FindAll().Where(x=> !distinctNames.Contains(x.Name)).ToList();
var resultList = tab1.Concat(tab2).OrderBy(x=>x.Name);
I have a dataset with following table
id domain email
1 google.com av#et.org
2 apple.com gopal#jt.com
3 abc.com av#et.org
4 global.com av#et.org
5 local.com gopaljite#gmail.com
6 xyz.com gopaljite#gmail.com
7 gpl.com gopal#jt.com
8 mno.com av#et.org
9 pqr.co.in gopaljite#gmail.com
10 aad.com av#et.org
Now I need output as following
Sr. Email Domain_Count
1 av#et.org 5
2 gopal#jt.com 2
3 gopaljite#gmail.com 3
How can I do this with LINQ ?
Thanks in advance.
var result=table.GroupBy(c => c.email)
.Select(group => new {
Email = group.Key,
Domain_Count = group.Count()
}).OrderByDescending(x=> x.Domain_Count);
bind to GridView:
yourgidView.DataSource = result.ToList();
I have a usage table which stores daily usage for customers of various products. I want to now return a result which groups results by customerID for total / combined usage of various products.
See below a perfectly illustrated example of the current data structure :)
id | customerID | prod1 | prod2
1 . 123 . 0 . 1
2 . 125 . 5 . 5
3 . 125 . 1 . 1
I am looking to return a result set as such (again, admire my illustrating ability):
customerID | prod1 | prod2
123 . 0 . 1
125 . 6 . 6
Will this kind of calculation be possible using EF? I am trying to avoid a multitude of loops to achieve the same thing so this would greatly help a brother out.
What you need is GroupBy and Sum:
var result = context.Customers
// .Where(filter) // filter if needed
.GroupBy(m => m.CustomerID)
.Select(g => new
{
CustomerID = g.Key, // We grouped according to CustomerID, so key = CustomerID
SumProd1 = g.Sum(m => m.Prod1), // Sum Prod1 of grouped data
SumProd2 = g.Sum(m => m.Prod2) // Sum Prod2 of grouped data
})
.ToList();
Note: ToList() is for retrieving data, it is not needed if you plan to work on query.