GroupBy and Sum - c#

I read a lot of GroupBy + Sum topics but I didn't understand how to use it.
I have a list of contacts, and in this list, i want to get the state (which appears more).
So my code is:
contacts.GroupBy(i => i.Address.State.ToUpperInvariant());
In this GroupBy, I want to know the state that appears more (and remove the case of "" because empty state is not important to me).
How do I do it?
I was thinking in something like this:
contacts.GroupBy(i => i.Address.State.ToUpperInvariant()).Select(i => i.Max());
Thanks in advance!

You want something like:
var counts = contacts
.Where(c => c.State != string.Empty)
.GroupBy(i => i.Address.State, StringComparer.OrdinalIgnoreCase)
.Select(grp => new { State = grp.Key, Count = grp.Count());
GroupBy returns an IEnumerable<IGrouping<TKey, TSource>>. Since IGrouping<TKey, TSource> implements IEnumerable<TSource>, you can use the Count extension method to get the number of elements in the group.

Related

How to do in this in Linq C#

So far, I have this:
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x)));
Configuration folder will contain pairs of files:
abc.json
abc-input.json
def.json
def-input.json
GetReportName() method strips off the "-input" and title cases the filename, so you end up with a grouping of:
Abc
abc.json
abc-input.json
Def
def.json
def-input.json
I have a ReportItem class that has a constructor (Name, str1, str2). I want to extend the Linq to create the ReportItems in a single statement, so really something like:
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x)))
**.Select(x => new ReportItem(x.Key, x[0], x[1]));**
Obviously last line doesn't work because the grouping doesn't support array indexing like that. The item should be constructed as "Abc", "abc.json", "abc-input.json", etc.
If you know that each group of interest contains exactly two items, use First() to get the item at index 0, and Last() to get the item at index 1:
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x)))
.Where(g => g.Count() == 2) // Make sure we have exactly two items
.Select(x => new ReportItem(x.Key, x.First(), x.Last()));
var v = Directory.EnumerateFiles(_strConfigurationFolder)
.GroupBy(x => GetReportName(Path.GetFileNameWithoutExtension(x))).Select(x => new ReportItem(x.Key, x.FirstOrDefault(), x.Skip(1).FirstOrDefault()));
But are you sure there will be exactly two items in each group? Maybe has it sence for ReportItem to accept IEnumerable, not just two strings?

GroupBy, OrderByDescending using Lambda in Linq

I have a collection and I need to order it by descending date(DateProcessed field) using Linq, first I am grouping by two possible Keys: Booked or Empty. But the data it's not being ordered..
This is my expression:
IEnumerable<IGrouping<string, MyClassMongo>> sub = Model.MyCollection.GroupBy(f => f.IsBooked ? "Booked" : "Empty").OrderByDescending(f => f.FirstOrDefault().DateProcessed);
I'm confused because I am grouping first, I know that after grouping the collection is splitted in two(Booked and Empty) so I am not sure how to handle the sorting because I am grouping first
If you are querying in-memory collection, then just place ordering before grouping:
IEnumerable<IGrouping<string, MyClassMongo>> sub =
Model.MyCollection
.OrderByDescending(f => f.DateProcessed)
.GroupBy(f => f.IsBooked ? "Booked" : "Empty");
Items within each group will be sorted by DateProcessed.
if you want to first Group by and then sort the results within the Group. you can try some thing like below
Model.MyCollection
.GroupBy(f => f.IsBooked,
(f, g) => new{Key=f.DateProcessed, Group = g.OrderByDescending(c=>c.IsBooked)})
.OrderByDescending(f => f.Key);
PS: Code may have syntax issue not tested but the idea should be to create the key withing each group so we can order by

Remove every first element of grouped collection

I have a collection of elements and some of these elements are duplicating. I need to extract all records but only the first record if the record is one of a duplicate set.
I was able to group the elements and find all elements that have duplicates, but how to remove every first element of a group?
var records =
dbContext.Competitors
.GroupBy(x => x.Email)
.Select(x => new { Properties = x,
Count = x.Key.Count() })
.Where(x => x.Count > 1)
.ToList();
EDIT: Seems like it's impossible to accomplish this task with EF, because it fails to translate the desired linq expression to SQL. I'll be happy if someone offer different approach.
To exclude the first record from each email-address group with more than one entry, you could do this:
var records = dbContext.Competitors
.GroupBy(x => x.Email)
.SelectMany(x => (x.Count() == 1) ? x : x.OrderBy(t=>t).Skip(1))
.ToList();
This is the logic :
Group by a property > Select every Group > (Possibly) Sort that > Skip first one
This can be turned into some linq code like this :
//use SelectMany to flat the array
var x = list.GroupBy(g => g.Key).Select(grp => grp.Skip(1)).SelectMany(i => i);

GroupBy and OrderBy

I'm trying to do a GroupBy and then OrderBy to a list I have. Here is my code so far:
reportList.GroupBy(x => x.Type).ToDictionary(y=>y.Key, z=>z.OrderBy(a=>a.Lost));
With the help of the last question I asked on linq I think the ToDictionary is probably unneeded, but without it I don't know how to access the inner value.
To be clear, I need to GroupBy the Type property and want the inner groups I get to be OrderBy the Lost property (an integer). I want to know if there is a better, more efficient way or at the least better then what I've done.
An explanation and not just an answer would be very much appreciated.
Yes, there is better approach. Do not use random names (x,y,z,a) for variables:
reportList.GroupBy(r => r.Type)
.ToDictionary(g => g.Key, g => g.OrderBy(r => r.Lost));
You can even use long names to make code more descriptive (depends on context in which you are creating query)
reportList.GroupBy(report => report.Type)
.ToDictionary(group => group.Key,
group => group.OrderBy(report => report.Lost));
Your code does basically the following things:
Group elements by type
Convert the GroupBy result into a dictionary where the values of the dictionary are IEnumerables coming from a call to OrderBy
As far as the code correctness it is perfectly fine IMO, but maybe can be improved in term of efficiency (even if depends on your needs).
In fact, with your code, the values of your dictionary are lazily evaluated each time you enumerate them, resulting in a call to OrderBy method.
Probably you could perform it once and store the result in this way:
var dict = reportList
.GroupBy(x => x.Type)
.ToDictionary(y => y.Key, z => z.OrderBy(a => a.Lost).ToList());
// note the ToList call
or in this way:
var dict = reportList.OrderBy(a => a.Lost)
.GroupBy(x => x.Type)
.ToDictionary(y => y.Key, z => z);
// here we order then we group,
// since GroupBy guarantees to preserve the original order
Looks fine to me. If you use an anonymous type instead of a Dictionary, you could probably improve the readability of the code that uses the results of this query.
reportList.GroupBy(r => r.Type)
.Select(g => new { Type = g.Key, Reports = g.OrderBy(r => r.Lost) });

Sorting an ArrayList

Based on my previous question, I've trying now to have them in the following order using the same approach, OrderByDescending and ThenBy
Original (can be in any random order):
1:1
0:0
0:1
2:1
1:0
2:0
Output
2:0
1:0
0:0
2:1
1:1
0:1
as you can see, a is descending, and b being ascending. But I'm still not getting the right sort. Any ideas why? Thanks
Think to what you would do manually:
First you must sort the values by the 2nd part in ascending order
Then you must sort values having the same 2nd part, using the 1st part in descending order
Translated in LINQ it's pretty the same:
var sorted = arrayList
.Cast<string>()
.Select(x => x.Split(':'))
.OrderBy(x => x[1])
.ThenByDescending(x => x[0])
.Select(x => x[0] + ":" + x[1]);
To clarify a bit more, ThenBy/ThenByDescending methods are used to sort elements that are equal in the previous OrderBy/OrderByDescending, hence the code :)
arrayList.ToList().Select(i => { var split = i.Split(":".ToArray(),2));
return new { a = Int32.Parse(split[0]),
b = Int32.Parse(split[1}) };
})
.OrderByDescending(i => i.a)
.ThenBy(i => i.b)
From your question it is not clear whether you want the order-by's reversed (just swap them).
Work from there, perhaps rejoining
.Select(i => String.Format("{0}:{1}", i.a, i.b));
Good luck

Categories