LINQ - Need help with a statement/ scenario - c#

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);

Related

How to select the most repeating records in related tables with Entity Framework

My tables here :
In C#
I want to know most repeated 3 categoryName and their count in Blog table, any idea? Thanks
var result = blogs.GroupBy(b => b.CategoryID)
.OrderByDescending(g => g.Count())
.Take(3)
.Select(x => new {CategoryName = x.First().Category.CategoryName, Count = x.Count()})
.ToList();
This will group your blogs by CategoryID, order by count of each grouping, take the top 3 and then select the category name and count of each group as a list.
list.GroupBy(x => x.CategoryName).Select(x => new { x.Key, count = x.Count() }).OrderBy(x => x.count).Take(3);
This will first Group the items by the name.
Then create an anonymous object with the group key (the name) and the Count of all items in every group. Then you order by count and take the first 3.
You could group by category name, order by highest count first and pick the first three results. Example:
Blogs
.GroupBy(b => b.Category.CategoryName)
.OrderByDescending(g => g.Count())
.Take(3)
.Select(x => new { CategoryName = x.Key, Count = x.Count() });

How can I split a List<T> into two lists, one containing all duplicate values and the other containing the remainder?

I have a basic class for an Account (other properties removed for brevity):
public class Account
{
public string Email { get; set; }
}
I have a List<T> of these accounts.
I can remove duplicates based on the e-mail address easily:
var uniques = list.GroupBy(x => x.Email).Select(x => x.First()).ToList();
The list named 'uniques' now contains only one of each account based on e-mail address, any that were duplicates were discarded.
I want to do something a little different and split the list into two.
One list will contain only 'true' unique values, the other list will contain all duplicates.
For example the following list of Account e-mails:
unique#email.com
dupe#email.com
dupe#email.com
Would be split into two lists:
Unique
unique#email.com
Duplicates
dupe#email.com
dupe#email.com
I have been able to achieve this already by creating a list of unique values using the example at the top. I then use .Except() on the original list to get the differences which are the duplicates. Lastly I can loop over each duplicate to 'pop' it out of the unique list and move it to the duplicate list.
Here is a working example on .NET Fiddle
Can I split the list in a more efficient or syntactically sugary way?
I'd be happy to use a third party library if necessary but I'd rather just stick to pure LINQ.
I'm aware of CodeReview but feel the question also fits here.
var groups = list.GroupBy(x => x.Email)
.GroupBy(g => g.Count() == 1 ? 0 : 1)
.OrderBy(g => g.Key)
.Select(g => g.SelectMany(x => x))
.ToList();
groups[0] will be the unique ones and group[1] will be the non-unique ones.
var duplicates = list.GroupBy(x => x) // or x.Property if you are grouping by some property.
.Where(g => g.Count() > 1)
.SelectMany(g => g);
var uniques = list.GroupBy(x => x) // or x.Property if you are grouping by some property.
.Where(g => g.Count() == 1)
.SelectMany(g => g);
Alternatively, once you get one list, you can get the other one using Except:
var uniques = list.Except(duplicates);
// or
var duplicates = list.Except(uniques);
Another way to do it would be to get uniques, and then for duplicates simply get the elements in the original list that aren't in uniques.
IEnumerable<Account> uniques;
IEnumerable<Account> dupes;
dupes = list.Where(d =>
!(uniques = list.GroupBy(x => x.Email)
.Where(g => g.Count() == 1)
.SelectMany(u => u))
.Contains(d));

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() ;

c# - Linq Query to retrieve all objects with a max value

Currently I have a List of objects in which I need to find all occurrences that have the maximum value.
Currently my solution to this has been:
Foo maxFoo = list.OrderByDescending(foo => foo.A).First();
List<Foo> maxFoos = new List<Foo>();
foreach(Foo foo in list) {
if (foo.A.Equals(maxFoo.A)) {
maxFoos.Add(foo);
}
}
However I want to know if there is a way to do this in a single Linq expression.
All the resources I have read only refer to getting the max value for one object.
Note: For the time being, I want to know a solution which doesn't rely on MoreLinq
You can group by the property, then order the groups by key, and take the content of the first one, like this:
var res = list
.GroupBy(item => item.A)
.OrderByDescending(g => g.Key)
.First()
.ToList();
You could group by A, order the group, and get the elements in the first group, which corresponds to the elements with the max value of A:
list
.GroupBy(x => x.A)
.OrderByDescending(grp=> grp.Key)
.First()
.Select(x => x);
This works:
var maxFoos =
list
.OrderByDescending(foo => foo.A)
.GroupBy(foo => foo.A)
.Take(1)
.SelectMany(foo => foo)
.ToList();

LINQ query has me baffled

How would I write a LINQ query to do the following?
I have a database table with a schema like this:
ID - Int
Time - DateTime
RecordType - Int
Msg - String
I want to get the newest (using 'Time' field) record for each 'RecordType'
Another restriction is that I'm only interested in certain RecordTypes - those contained in an int array.
The result of the query would be one record per RecordType - the newest record for this type.
var results = source.GroupBy(x => x.RecordType)
.Where(g => myRecordTypes.Contains(g.Key))
.Select(g => g.OrderByDescending(x => x.Time).First())
.ToList();
myRecordTypes is int[] with a set of RecordTypes you'd like to get as a result.
result will be List<Record> with one item per RecordType.
You can change to it to be e.g. Dictionary<int, Recort> by RecordType:
var results = source.GroupBy(x => x.RecordType)
.Where(g => myRecordTypes.Contains(g.Key))
.Select(g => new { g.Key, item = g.OrderByDescending(x => x.Time).First() })
.ToDictionary(x => x.Key, x => x.item);
Group them by record types, filter out the ones you want, and then select out the first of the items in that group ordered by time.
int[] recordTypes = GetRecordTypes();
var query = context.Table.GroupBy(item => item.RecordType)
.Where(group => recordTypes.Contains(group.Key))
.Select(group => group.OrderBy(item => item.Time).FirstOrDefault());

Categories

Resources