Linq Query To Group All Other Than Top 3 - c#

I have the following code in Linq, and I was wondering how to make it so that it groups all others beside the top 3 into an others category and sum their volumes.
var list = (from t in sortedCollection.DataItem
orderby t.volume
select t).Take(3);

You need to use Skip to ignore top 3 and group the rest like:
var list = (from t in sortedCollection.DataItem
orderby t.volume
select t).Skip(3);
From the comments, it seems you only want to get the sum of a particular field after skipping first 3 records.
var sum = (from t in sortedCollection.DataItem
orderby t.volume
select t).Skip(3).Sum(r=> r.VOLUME);
Or with a complete method syntax:
var Sum = sortedCollection.DateItem.OrderBy(t => t.volume)
.Skip(3)
.Sum(r=> r.volume);
If you need grouping , that it would look like:
With method syntax it should be something like:
var query = sortedCollection.DateItem.OrderBy(t => t.volume)
.Skip(3)
.GroupBy(t => t.YourGroupingField);
To do Sum based on a field you can do something like:
var query = sortedCollection.DateItem.OrderBy(t => t.volume)
.Skip(3)
.GroupBy(t => t.YourGroupingField)
.Select(grp => new SqlCommand(
{
Key = grp.Key,
Sum = grp.Sum(r=> r.ValueFieldForSum)
}));

Related

GroupBy then sum in separate expressions in Entity Framework

Usually when we do GroupBy and Sum in Entity Framework, we have something like this:
var query = from q in db
group q by q.Gender into g
select new
{
name = g.Key,
value = g.Sum(a => a.saving_amount)
};
If I want to separate the above queries and build GroupBy and select to Sum step by step, I have:
var q1 = from q in db
group q by q.Gender
var q2 = q1.Select (g=> new{
name = g.Key
value = g.Sum(a => a.saving_amount)
});
However if I do this, I get an error message
'object' does not contain a definition for 'saving_amount' and no accessible extension method 'saving amount'...
To make sure I do have the 'saving_amount' I output my result as so
var q1 = from q in db
group q by pc.Gender
var q2 = q1.Select (g=> new{
name = g.Key
value = g.ToList()
});
and confirmed my 'saving_amount' is a property in the list of objects. (below is a sample output through an API using Postman)
[{"name":"Female","value":[{"id":"xxxxxxxx","version":"R","issue_date":"2021-01-26T00:00:00","saving_amount":100000.0000,...
My question is how can I sum the 'saving_amount' under the separated query?
Thank you.
Looks like I've resolved it by using below instead
var q1 = from q in db
group q by q.Gender
var q2 = q1.Select (g=> new{
name = g.Key
value = g.Select(a => a.saving_amount).Sum()
});

How to filter a sublist inside parent and return parent with sublist filtered

I want to create a linq to sql query that will return a list of objects with a sublist that has been filtered.
It sounds easy but I'm not sure how to make this to work
Here the SQL Query which returns what I want:
select * from Texts t inner join Translations tt on t.TranslationId = tt.Id
inner join Pages p on tt.Id = p.TranslationId and tt.NeutralText = p.TitleNeutralTextId
where t.LanguageId = 1
Now I have to write this with linq.
What I've done so far is:
var query = this.Queryable() // Page entity
.AsNoTracking()
.Include(x => x.TitleTranslation.Texts);
return (from m in query
from l in m.TitleTranslation.Texts
where m.TitleTranslation.Texts.Any(l => l.LanguageId == 1)
select m);
But it didn't work because I got the sublist with all languages instead of language with id #1 only.
Thanks for helping,
David
Any specific reason you are writing query? Either you can use Eager Loading of EF to load all the child tables, Or below Linq statement can fetch the required result
var result = texts.Join(translations, t => t.TranslationId, tt => tt.Id, (t, tt) => new {t, tt})
.Join(pages, ttt => new { Id = ttt.tt.Id, NeutralTextId = ttt.tt.NeutralText }, p => new { Id = p.TranslationId, NeutralTextId = p.TitleNeutralTextId }, (ttt, p) => new {ttt, p})
.Where(tttt => tttt.ttt.t.LanguageId == 1);
Here replace texts, translations and pages with actual dbContext entities collection property.
I think you must try lime this. this will work for you .
This will be similar to sql query
One way to do this .
var result = from m in Texts
join Translations on Texts.TranslationId = Translation.Id
Join Pages on Translations.NeutralText = Pages.NeutralText
where Texts.LanguageId = 1
select m
There an other way to do this using entity framework
var result =
this.Queryable().AsNoTracking().Include(x=>x.Translations).Where(x=>x.LanguageId= 1)
I found the solution I wanted thanks to Hasnain Bukhari.
The solution was to start from the text table, assign the filter, include the desired Entity (Page) and put the results into memory (ToList()). Then select pages. It will give the result I want in the order I have to.
var query = textService.Queryable()
.AsNoTracking()
.Include(x => x.Translation.Pages)
.Where(x => x.LanguageId == languageId).ToList();
return query.SelectMany(x => x.Translation.Pages);

Convert MSSQL Query to Lambda Expressions or LINQ. Grouping Sum and Sorting

I have a SQL Query to extract sorted data by sum of a column value. I have a table named CustomerPoint and It has 2 columns named CusTomerID and Point , I want to extract the person with the highest SUM of points. Here is my SQL Query and it runs properly. But I need to execute it in EF6.3 with LambdaExpressions or Linq queries.
SELECT SUM(Point) AS POINTS, CustomerID FROM CustomerPoint AS POINTS GROUP BY CustomerID ORDER BY POINTS
Thank you for your help!
Something like this:
from p in context.Points
group p by p.CustomerID into gr
select new { CustomerID = gr.Key, POINTS = gr.Sum(c=>c.Point) } into re
orderby re.POINTS
Please try this.
from cp in db.CustomerPoints
Group cp by cp.CusTomerID into cpg
select new { CusTomerID = cpg.Key, Points = cpg.Sum(c => c.Point)}
orderby cpg.Points
And lambda form for diversity:
var query = youContext.CustomerPoints
.GroupBy(x => x.CustomerID)
.Select(x => new { Points = x.Sum(y => y.Point),
CustomerID = x.Key })
.OrderBy(x => x.Points);

Select most frequent value and count using LINQ and assign to dictionary

I'm trying to select the top five most frequent values and their count in my table and return them in a Dictionary. I'm able to get the values in sql:
SELECT top 5
SR_Status,
COUNT(SR_Status) AS 'value_count'
FROM
ServiceRequests
GROUP BY
SR_Status
ORDER BY
'value_count' DESC;
How to convert to linq and assign to Dictionary
You do not specify if you are using Linq2Sql or Linq2Objects, so, let's just assume linq then. Try something like this (see the comments for each line):
var result = (from s in ServiceRequests // define the data source
group s by s.SR_Status into g // group all items by status
orderby g.Count() descending // order by count descending
select new { g.Key, Total = g.Count() }) // cast the output
.Take(5) // take just 5 items
.ToDictionary(x => x.Key, x => x.Total); // cast to dictionary
Obs: I didn't test it.
Assuming you are using Entity Framework and have an EntitySet named ServiceRequests and all the properties names are the same as the column names:
var result = context.ServiceRequests.GroupBy(sr => sr.SR_Status)
.Select(g => new { Key = g.Key, Count = g.Count() })
.OrderByDescending(kv => kv.Count)
.Take(5)
.ToList();

LINQ: how to get a group of a table ordering with a related table?

I have a doubt about the object IGrouping that results from a linq where I use a "group by" sentence.
I have two tables in the database, Products and Responses they have a relationship 1 to *. In the Responses table we have a column called FinalRate which is the rate of the product. The products can have n responses or rates.
I want to get the Products order by the sum of the FinalRate divided by the number of rates done. That is to say, order by the average rate descending from higher to lower marks.
As it can be read in the code (at the end of the question), I try to get the responses first. To sum all the finalrates and divide them by the count I use a group.
There are 2 problems with the code, even if the current code works:
1.-I tried to get the Products in a single query but it is impossible because I can not use the products table in the group and then use the Response table in the "orderby". One more thing LINQ only gives you the possibility to group one table, it is imposible to have "group prod, response".
I couldn't get this sql sentence in LINQ:
select prod.ProductID,prod.Commercial_Product_Name,prod.Manufacturer_Name,
prod.ProductImageUrl
from rev_product prod
inner join rev_response res on res.AtProductid=prod.ProductID
group by prod.ProductID,prod.Commercial_Product_Name,prod.Manufacturer_Name
,prod.ProductImageUrl
order by (sum(res.FinalRate)/count(res.AtProductid))
I tried this:
var gruposproductos = (from prod in ctx.Products
join res in ctx.Responses on prod.ProductID equals res.AtProductId
group prod by prod.ProductID into g
orderby (g.Sum(ra =>ra.FinalRate)/g.Count())
descending select g).Take(2);
But as I say, the "orderby (g.Sum..." gives an error, because "into g" groups the Product table, not the Response Table.
So this is why in my final code I don't get the products in the same LINQ sentence.
2.-Once accepted this fact, the problem is that I get an IGrouping, but I don't obtain a list of Responses that I can iterate without doing the two foreach in the code. I wanted only one loop, as one would do if you had a "List" object.
It is not really a cool method but it works. Moreover, I have to control that in the second loop there is only added 1 time.
Any better code?
var groupproducts = (from res in ctx.Responses
group res by res.AtProductId into g
orderby (g.Sum(ra =>ra.FinalRate)/g.Count())
descending select g).Take(2).ToList();
List<Product> theproducts = new List<Product>();
foreach (var groupresponse in groupproducts)
{
foreach (var response in groupresponse)
{
var producttemp= (from prod in ctx.Products
where prod.ProductID == response.AtProductId
select prod).First();
theproducts.Add(producttemp);
}
}
}
FINAL SOLUTION (thx a lot #Daniel)
var productsanonymtype = ctx.Products.Select(x => new
{
Product = x,
AverageRating = x.Responses.Count() == 0 ? 0 : x.Responses.Select(r => (double)r.FinalRate).Sum() / x.Responses.Count()
}).OrderByDescending(x => x.AverageRating);
List<Product> products = new List<Product>();
foreach (var prod in productsanonymtype)
{
products.Add(prod.Product);
}
Try this:
products.Select(x => new
{
Product = x,
AverageRating = x.Responses.Sum(x => x.FinalRate) /
x.Responses.Count()
});
The Sum overload I am using is not implemented in all providers. If that's a problem for you, you can use this alternate version:
products.Select(x => new
{
Product = x,
AverageRating = x.Responses.Select(x => x.FinalRate)
.Sum() /
x.Responses.Count()
});
If there is no navigation property from product to its responses you should first try to fix that. If you can't you can use this version:
products.Join(responses, x => x.Id, x => x.ProductId,
(p, r) => new { Product = p, Response = r })
.GroupBy(x => x.Product)
.Select(g => new { Product = g.Key,
AverageRating = g.Select(x => x.Response.FinalRate)
.Sum() /
g.Count()
});
Assuming FinalRate is an int, both methods will calculate the average rating with an int, i.e. there will be no 4.5 rating. And there will be no rounding, i.e. an actual average rating of 4.9 will result in 4. You can fix that by casting one of the operands of the division to double.
Another problem is the case with no ratings so far. The code above will result in an exception in this case. If that's a problem for you, you can change the calculation to this:
AverageRating = g.Count() == 0
? 0
: g.Select(x => (double)x.Response.FinalRate).Sum() / g.Count()
ctx.Products.GroupBy(x => new {
ProductId = x.ProductId,
FinalRate = x.Responses.Sum(y => y.FinalRate),
CountProductId = x.Responses.Count
})
.OrderBy(x => x.Key.FinalRate / x.Key.CountProductId);
And here with the projection.....
ctx.Products.Select(x => new {
ProductID = x.ProductID,
Commercial_Product_Name = x.Commercial_Product_Name,
Manufacturer_Name = x.Manufacturer_Name,
ProductImageUrl = x.ProductImageUrl,
FinalRate = x.Responses.Sum(y => y.FinalRate),
CountProductId = x.Responses.Count
})
.GroupBy(x => new {
ProductId = x.ProductId,
FinalRate = x.FinalRate,
CountProductId = x.CountProductId
})
.OrderBy(x => x.Key.FinalRate / x.Key.CountProductId);

Categories

Resources