Alphabetic GroupBy in Linq with a twist - c#

I have a tricky question. I'm looking for the most concise, hackiest way of achieving the following:
query = (from book in library.books.OrderBy(x=>x.title)
group book by
new { title = book.title[0].ToString(), yadiyada="" });
The result of which is all of the books in the library grouped by the first letter. Yadiyada is because my group object is not a simple string but an object.
I'm wondering if there's a pure LINQ way of making it so that the grouping is 'A', 'B', 'C', ... 'Z', but all others fall into a single grouping called '123!##'.
In other words, I want only one grouping for all non alpha characters (A->Z + Rest).
I can do this in many ways if I get verbose (currently I'm simply making a union of two Linq statements), but that's not the purpose of this question. I'm wondering if someone can come up with a really neat way of doing it...

It depends on what a pure LINQ way means. If you want to group it with a single query, you can try something like this:
query = (from book in library.books.OrderBy(x=>x.title)
let c = book.title[0]
group book by
new { title = char.IsLetter(c) ? c.ToString() : "123!##", yadiyada="" });

I think you want some kind of conditional grouping. You could use the null character as placeholder for all non alpha characters:
HashSet<char> alpha = new HashSet<char>("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvqxyz");
var query = books
.OrderBy(b => b.title)
.Select(b => new{ Book = b, IsAlpha = alpha.Contains(b.title[0]), Char = b.title[0]} )
.Select(x => new{ x.IsAlpha, x.Book, Char = x.IsAlpha ? x.Char : '\0' } )
.GroupBy(x => x.Char);

Related

Does my LINQ query provide distinct SampleNumbers based off the code and LINQPad output provided?

I'm trying to use LINQPad to see outputs of my queries I'm writing for a C# WinForms application. I'm more just confused on how to interpret the outcome on a Dump() method and what is happening behind the scenes.My intended outcome is to have one SampleNumber per group of SampleData. SampleData is coming out of the database as a string split by commas.
Also, please spare me; I know how I name stuff is horrible and I'm planning to adjust once I get the data coming out correctly!
Here is my initial query:
var thinthroughoutQuery = (from a in ThicknessBlobs
join b in SummaryDataItems on a.SampleNumber equals b.SampleNumber
where (a.Source == "Thickness[0,0]")
&& b.TopBottomStatusFK == 3
orderby a.PKId descending
select a).Take(1000).ToList();
Here is where I attempt to take the string of SampleData, cut it at the delimiter, group by SampleNumber, and parse it as an integer:
var intThinThroughoutQuery = thinthroughoutQuery.Select(row => new { SampleNumber = row.SampleNumber, Value = row.SampleData.Split(",").Select(int.Parse) })
.SelectMany(group => group.Value.Select(value => new { group.SampleNumber, SampleData = value })).ToArray();
Here is the output from using Dump() in LINQPad:
To me, this appears like there are not distinct SampleNumbers and that each piece of SampleData separately maps to a repeating SampleNumber.
For further clarification, I want to be able to access the data and have it be like this:
Rather than:
You are missing GroupBy/ToLookup after flattening with SelectMany:
var intThinThroughoutQuery = thinthroughoutQuery.Select(row => new { SampleNumber = row.SampleNumber, Value = row.SampleData.Split(",").Select(int.Parse) })
.SelectMany(group => group.Value.Select(value => new { group.SampleNumber, SampleData = value }))
.ToLookup(row => row.SampleNumber, row => row.SampleData);

Split List into multiple lists of similar values C# [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have a parent list something like so :-
ParentList = {a,b,c,a,c,d,b,a,c,c}
I want to split this list into smaller list something like :-
ListA = {a,a,a} ListB = {b,b} ListC= {c,c,c,c} ListD = {d}
My main intention is to get the count of the highest occurring value. In the case above it would be 4 which is the count of ListC.
How can I split the parent list into small list like stated in example. Or is there a way I can get the greatest count without the list splitting.
Any help is appreciated.
Use GroupBy to group similar values and then count the amount of items in each group:
var result = ParentList.GroupBy(item => item)
.Select(group => new {
Key = group.Key,
Count = group.Count() })
.OrderByDescending(item => item.Count);
You can also use query syntax:
var result = from item in ParentList
group 1 by item into g
order by g.Count() descending
select new { Key = g.Key, Count = g.Count() };
If you really want different collections with different variables to them, as in your description above then you need to retrieve from the snippet each collection. You can also use ToDictionary on the result of the grouping.
Assuming you just want the count, and not which character/string gives that count, here is a one liner (you'll need using System.Linq;)
var highestCount = ParentList.GroupBy(p => p).Max(p => p.Count());
string maxRepeated = ParentList.GroupBy(s => s)
.OrderByDescending(s => s.Count())
.First().Key;
A simple way to do this would be using LINQ
var chars = new[] { 'a', 'b', 'c', 'a', 'c', 'd', 'b', 'a', 'c', 'c' };
var largetstGroup = chars
.GroupBy(_ => _) // Group items by the letter this will yield groups (aka lists) that will conatin only each letter
.OrderByDescending(_ => _.Count()) //Order by the items in each list
.FirstOrDefault(); // get the first one
If you have more complex objects in your list, where the GroupBy method is used you can specify any property to perform the grouping by (ex, for a list of Persons you could group by age GroupBy(_=> _.Age)

What are the two ways I can write this statement with LINQ?

I understand that there are two different ways I can write LINQ code. Can someone show me the two ways for this simple code block. Which is the most commonly used or considered most easy to debug
var subTopics = _subTopicService.GetSubTopics(Id);
var subTopicsSelect = (from subTopic in subTopics
select new
{
id = subTopic.SubTopicId,
name = subTopic.Name
});
Since your query consists solely of a from and select clause, all you need to do to convert this to fluent syntax is call .Select.
In fluent syntax, that would be:
var subTopicsSelect = subTopics.Select(x =>
new
{
id = x.SubTopicId,
name = x.Name
});
Further Reading
How to: Write LINQ Queries in C#
You have displayed the first way "SQL-like syntax" the second would be "Lambda syntax":
subTopics.Select(s => new { id = s.SubTopicId, name = s.Name });
This really confuses me as I have a completely different way to select here are the two methods:
var emailsToSend = db.emailQueues.Where(
e => e.sent == false
).Take(5);
var emailsToSend2 = from e2 in db.emailQueues
.Take(5)
.Where(
e => e.sent == false
)
select e2;
They both seem to do exactly the same thing but i prefer the syntax of the first method. Its easier to remember.

Group by multiple tables and still have access to the original query?

I was trying to do a join and then a group by, My grouped information that is returned is great! works like a charm, but i still need access to values outside the grouping, if that makes sense..
I found an example on stackoverflow, which probably explains it better
var query = from c in context.Contacts
join o in context.Orders on c.Id equals o.CustomerId
select new
{
Contact = c,
Order = o
} into ContactAndOrder
group ContactAndOrder by ContactAndOrder.Order.Id into g
select new
{
g.Key,
ContactWhatever = g.Sum(co => co.Contact.Whatever),
OrderWhatever = g.Sum(co => co.Order.Whatever)
};
Now this seems to work great, problem is in my situation the co.Order.Whatever is a string so i get an error saying can't convert string to int, this i understand as the aggregate function Sum expects a int....
My question really is, is there an aggregate function or something similar to i can get the value of co.Order.Whatever (a string in my case )
The problem being is once the group by has been done i lose "c" and "o"
I hope someone can help.
thanks in advance
if it's always the same in the group, the First will be enough...
ContactWhatever = g.First().Contact.Whatever
else use #Andrei Answer
or
ContactWhatever = g.Select(o => o.Contact.Whatever).Aggregate((a,b) => a + "," + b)

Linq: using StringComparer with GroupBy/Distinct in query syntax

I have this (XLinq) query and was wondering how to convert it to the query syntax:
var grouped = doc.Descendants()
.GroupBy(t => t.Element(ns + "GroupingAttr").Value, StringComparer.OrdinalIgnoreCase);
This is the query syntax without the StringComparer:
var grouped = from t in doc.Descendants()
group t by t.Element(ns + "GroupingAttr").Value into group
select group
My groupby is a little more complicated than this, so I prefer to use the key of the group instead of introducing a new property.
This is what I tried, but doesn't work because the let "key" is not available in the context of the select (I've uses my more complicated key definition to illustrate the fact I don't want to repeat this in the select):
var grouped = from t in doc.Descendants()
let key = ((t.Name != ns + "SomeElementName") ? t.Element(ns + "SomeAttribute") : t.Element(ns + "SomeOtherAttribute")).ElementValueOrDefault("Empty group")
group t by key.ToUpper() into g
select new { Name = key, Items = g };
In the end, case-sensitivity was not important because I could presume that all casings were the same...
Related question: LINQ Distinct operator ignore case?
I don't think you can use the comparer within the query syntax, however you could call ToUpper on your value. This will then ignore case for you. As a side note using ToUpper is more efficient than using ToLower, so ToUpper would be the way to go.
The C# team were very sparse with what they introduced into the query syntax, so for anything like this you'll have to use the extension methods syntax.
var grouped = from t in doc.Descendants()
group t by t.Element(ns + "GroupingAttr").Value into MyGroup
select MyGroup.Key

Categories

Resources