MongoDB GroupBy Aggregate and Count Documents C# - c#

I have a requirement to get the count of documents based on status of customer. So I need to use aggregate function and then group by based on status. I have used following code for that but the problem is that in Result I am getting the list of documents but what I just want to have is the status and count of documents under that. Can any body please help in adjusting the query to achieve the results.
var result = collection.Aggregate()
.Group(
x => x.status,
g => new
{
Result = g.Select(x => new CustomerDetailsList
{
ActiveType = x.status,
Count = g.Count()
}
)
}
);
Thanks in advance

The reason you're getting a list of documents for every key is that you're running this nested Select, all you need is:
collection.Aggregate()
.Group(
x => x.status,
g => new CustomerDetailsList
{
ActiveType = g.Key,
Count = g.Count()
}).ToList();

I am satisfied with the answer of #mickl and it works well as I tested according to my requirement but here is the way I opted in my app as this is what I am comfortable with. The method is to use the collection as queryable
var result = collection.AsQueryable()
.GroupBy(x => x.status)
.Select(x => new CustomerDetailsList
{
ActiveType = x.Key, Count = x.Count()
}).ToList();
I have used more LINQ in this way so I choose this as it's better to understand for me.
You can choose any of the methods either this or as demonstrated by #mickl

Related

C# LINQ Group by

I'm new to C# and trying to answer some LINQ questions. I'm stuck on 1st marked as difficult...
Q: What were the top 10 origin airports with the largest average​ departure delays, including the values of these delays? (Hint: use group by)?
I have a list named "Flights" populated with more than 20000 objects of class "FlightInfo".
Properties of the FlightInfo class are:
string Carrier, string Origin, string Destination, int DepartureDelay, int ArrivalDelay, int Cancelled, int Distance.
I understand that I should group FlightInfo by FlightInfo.Origin and than average each of these groups by FlightInfo.DepartureDelay and than show 10 with the highest average delay, but beside grouping I'm completely stuck on how to proceed further.
Thank you in advance for any help!
Here is the example of one of previous questions that I was able to answer:
Q: The weighted arrival delay of a flight is its arrival delay divided the distance. What  was the flight with the largest weighted arrival delay out of Boston, MA?
A:
var weighted = (from FlightInfo in Flights
where FlightInfo.Origin == "Boston MA"
orderby (FlightInfo.ArrivalDelay / FlightInfo.Distance) descending
select FlightInfo).Take(1);
var topTen = flights.
GroupBy(g => g.Origin).
Select(g => new { Origin = g.Key, AvgDelay = g.ToList().Average(d => d.DepartureDelay) }).
OrderByDescending(o => o.AvgDelay).
Take(10);
var result = flights
.GroupBy(f => f.Origin)
.OrderByDescending(g => g.Average(f => f.DepartureDelay))
.Take(10)
.Select(g => new
{
AirportName = g.Key,
Flights = g.ToList()
});
The last .Select parameter depends on what you want.
You could do this.
var top10 = Flights.GroupBy(g=>g.Origin) // groupby origin
.OrderByDescending(x=> x.Sum(f=> f.ArrivalDelay / f.Distance)) // Get the weighted delay for each fight and use for ordering.
.Select(x=>x.Key) //Airport or Origin (Modify with what you want)
.Take(10)
.ToList() ;

Linq IEnumerable<IGrouping<string, Class>> back to List<Class>

How can I turn the following statement back to List<DocumentData>
IEnumerable<IGrouping<string, DocumentData>> documents =
documentCollection.Select(d => d).GroupBy(g => g.FileName);
the goal is to get List that should be smaller than documentCollection.
FileName contains duplicates so I want to make sure I don't have duplicate names.
I have also tried the following but it's still providing me with duplicate file names
documentCollection =
documentCollection.GroupBy(g => g.FileName).SelectMany(d => d).ToList();
Each IGrouping<string, DocumentData> is an IEnumerable<DocumentData>, so you could simply call SelectMany to flatten the sequences:
var list = documents.SelectMany(d => d).ToList();
Edit: Per the updated question, it seems like the OP wants to select just the first document for any given filename. This can be achieved by calling First() on each IGrouping<string, DocumentData> instance:
IEnumerable<DocumentData> documents =
documentCollection.GroupBy(g => g.FileName, StringComparer.OrdinalIgnoreCase)
.Select(g => g.First())
.ToList();
You haven't said what T should stand for in List<T> you're looking for, so here are couple the most likely to be desired:
List<DocumentData> - rather pointless as you already have that on documentCollection
var results = documents.SelectMany(g => g).ToList();
List<KeyValuePair<string, List<DocumentData>>
var results =
documents.Select(g => new KeyValuePair(g.Key, g.ToList())).ToList();
List<string> - just the names
var results = documents.Select(g => g.Key).ToList();
List<IGrouping<string, DocumentData>>
var results = documents.ToList();

Chained selects with where at the end

Given following structure: a person has functions. Each function has roles. Each roles has features. Now I would like to figure out with linq if a given person has a certain feature, but I am doing something wrong with this query. As a result I always get the count of the functions (but I'd like to get the count of the features):
var count = person.Functions
.Select(fu => fu.Roles
.Select(r => r.Features
.Where(f => f.FeatureId == 99999)))
.Count();
What am I doing wrong here? According to this query I expect either 0 (hasn't got the feature) or 1.
var query = from function in person.Functions
from role in function.Roles
from feature in role.Features
where feature.FeatureId == 99999
select feature;
var count = query.Count();
or
var count = person.Functions
.SelectMany(function => function.Roles)
.SelectMany(role => role.Features)
.Count(feature => feature.FeatureId == 99999);
If you don't need the exact count but just want to know if the person has the feature or not, use Any instead of Count.
var count = person.Functions
.SelectMany(p => p.Roles)
.SelectMany(r => r.Features)
.Where(f => f.FeatureId == 99999)
.Count();
I'm not really sure, but I think you want the total number of Features with teh given Id. You would want to use SelectMany.

Linq query problem

I have the following information
var details = Details.Where(d => d.isActive);
I would like to query another table, Authorizations, that has a FK to Details, and get the sum amounts of the authorizations that are contained within the details object grouped by the detail and a FundCode.
Details (1 to many) Authorizations
Seems simple enough, however I'm having a bit of trouble.
Here is what I currently have:
var account = (from sumOfAuths in Authorizations
where details.Contains(sumOfAuths.Details)
&& sumOfAuths.RequestStatusId == 2
group sumOfAuths by new { sumOfAuths.Detail, sumOfAuths.FundCode } into child
select new {
....
Amount = child.Amount
}).Sum()
The problem is that inside the .Sum() function I have collection of objects rather than 1. So I can't Sum the amounts properly.
Generally, you can specify what it is you want to sum:
.Sum(x => x.Amount)
In groups, you can use nested sums:
.Sum(x => x.Sum(y => y.Amount));
I believe this query produces what you're looking for:
var account = from c in Authorizations
where details.Contains(c.Details) && c.RequestStatusId == 2
group c by new { c.Detail, c.FundCode } into g
select new { Key = g.Key, Sum = g.Sum(x => x.Amount) };

LINQ - Need help with a statement/ scenario

Here's the scenario:
Given a List of Outputs each associated with an integer based GroupNumber. For each distinct GroupNumber within the List of Outputs starting with the lowest GroupNumber (1). Cycle through that distinct group number set and execute a validation method.
Basically, starting from the lowest to highest group number, validate a set of outputs first before validating a higher groupnumber set.
Thanks,
Matt
There's almost too many ways to solve this:
Here's one for a void Validate method.
source
.GroupBy(x => x.GroupNumber)
.OrderBy(g => g.Key)
.ToList()
.ForEach(g => Validate(g));
Here's one for a bool Validate method.
var results = source
.GroupBy(x => x.GroupNumber)
.OrderBy(g => g.Key)
.Select(g => new
{
GroupNumber = g.Key,
Result = Validate(g),
Items = g.ToList()
})
.ToList();
If you need them as groups:
var qry = source.GroupBy(x=>x.GroupNumber).OrderBy(grp => grp.Key);
foreach(var grp in qry) {
Console.WriteLine(grp.Key);
foreach(var item in grp) {...}
}
If you just need them ordered as though they are grouped:
var qry = source.OrderBy(x=>x.GroupNumber);

Categories

Resources