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) });
Related
could someone tell me the correct way to query this:
dictionary of dictionary
Dictionary<int, Dictionary<Guid, AutoStopWatchAndLog>> _dicDictionaryThread
where what i am looking for is from any of the first level and then from any item in the second where the level is less than x
dics betlow is: Dictionary<int, Dictionary<Guid, AutoStopWatchAndLog>>
var mostlikey = dics.FirstOrDefault(x=>x.Value.Where(y=>y.Value.Level > x));
If you want to project to a new dictionary of dictionaries filtered to the desired items, you will need to project both levels of dictionaries, which would look something like:
var query = _dicDictionaryThread.Select(o => new {o.Key, Value = o.Value
.Where(y=>y.Value.Level > x)
.ToDictionary(y => y.Key, y => y.Value)})
.Where(o => o.Value.Any())
.ToDictionary(o => o.Key, o => o.Value);
If you can easily understand this and explain it to someone else, go for it, otherwise just use a traditional loop - you're not going to get any performance boost from Linq and it will likely take longer to decipher.
The following works in VB..
Dim q = allValues.GroupBy(Function(u) u.R).Select(Function(grp) grp).OrderByDescending(Function(a) a.Count).ToList
But not in C#..
dynamic q = allValues.GroupBy(u => u.R).Select(grp => grp).OrderByDescending(a => a.Count).ToList;
"allValues" is a list of color of various pixels. I'm trying to group the color R value and sort by the count descending to find the most used color.
I'm a noob to C# and Lambda. This is also my first post to the site. Thanks for any insite!
a.Count is a method, it needs parentheses -
.OrderByDescending(a => a.Count())
So does ToList() for that matter.
And a couple of little side issue:
Select(grp => grp) is pointless, you can just remove that bit.
The result does not need to be dynamic - you can just use var.
The right syntax is
var q = allValues // probably you mean "var" instead of "dynamic"
.GroupBy(u => u.R)
.Select(grp => grp) // that's redundant, you can remove it
.OrderByDescending(a => a.Count())
.ToList();
note () after Count and after ToList. "()" means that you call the method.
This is related to my other question here. James World presented a solution as follows:
// idStream is an IObservable<int> of the input stream of IDs
// alarmInterval is a Func<int, TimeSpan> that gets the interval given the ID
var idAlarmStream = idStream
.GroupByUntil(key => key, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
<edit 2:
Question: How do I start the timers immediately without waiting for the first events to arrive? That's the root problem in my question, I guess. For that end, I planned on sending off dummy objects with the IDs I know should be there. But as I write in following, I ended up with some other problems. Nevertheless, I'd think solving that too would be interesting.
Forwards with the other interesting parts then! Now, if I'd like to group a complex object like the following and group by the key as follows (won't compile)
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
then I get into trouble. I'm unable to modify the part about SelectMany, Concat and Observable.Return so that the query would work as before. For instance, if I make query as
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key.First())))
.Subscribe(i => Console.WriteLine(i.Id + "-" + i.IsTest);
Then two events are needed before an output can be observed in the Subscribe. It's the effect of the call to First, I gather. Furthermore, I woul like to use the complex object attributes in the call to alarmInterval too.
Can someone offer an explanation what's going on, perhaps even a solution? The problem in going with unmodified solution is that the grouping doesn't look Ids alone for the key value, but also the IsTest field.
<edit: As a note, the problem probably could be solved firsly by creating an explicit class or struct and then that implements a custom IEquatable and secondly then using James' code as-is so that grouping would happen by IDs alone. It feels like hack though.
Also, if you want to count the number of times you've seen an item before the alarm goes off you can do it like this, taking advantage of the counter overload in Select.
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key))
.SelectMany(grp => grp.Select((count, alarm) => new { count, alarm }).TakeLast(1));
Note, this will be 0 for the first (seed) item - which is probably what you want anyway.
You are creating an anonymous type in your Select. Lets call it A1. I will assume your idStream is an IObservable. Since this is the Key in the GroupByUntil you do not need to worry about key comparison - int equality is fine.
The GroupByUntil is an IObservable<IGroupedObservable<int, A1>>.
The SelectMany as written is trying to be an IObservable<A1>. You need to just Concat(Observable.Return(grp.Key)) here - but the the type of the Key and the type of the Group elements must match or the SelectMany won't work. So the key would have to be an A1 too. Anonymous types use structural equality and the return type would be stream of A1 - but you can't declare that as a public return type.
If you just want the Id, you should add a .Select(x => x.Id) after the Throttle:
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key)
.Select(x => x.Id))
.SelectMany(grp => grp.IgnoreElements().Concat(Observable.Return(grp.Key)));
If you want A1 instead - you'll need to create a concrete type that implements Equality.
EDIT
I've not tested it, but you could also flatten it more simply like this, I think this is easier! It is outputing A1 though, so you'll have to deal with that if you need to return the stream somewhere.
var idAlarmStream = idStream
.Select(i => new { Id = i, IsTest = true })
.GroupByUntil(key => key.Id, grp => grp.Throttle(alarmInterval(grp.Key))
.SelectMany(grp => grp.TakeLast(1));
I need to convert my city list into group by state and order by city within it.
I tried below one but not able to get it right. Would appreciate any help on this.
cities.GroupBy(g => g.state).Select(o => o.OrderBy(c => c.cityname));
Try below code
cities.GroupBy(g => g.state)
.Select(o =>new {
State = o.Key,
Cities = o.OrderBy(c => c.cityname).Tolist()})
.Tolist();
cits.OrderBy(d => d.cityname).GroupBy(d => d.state).SelectMany(g => g).ToList();
1 - Order by cityname first.
2 - Then group them according to state. Since you order first, groups are still ordered with respect to cityname property.
3 - Convert to single list. Otherwise, you will end up with list of groups.
Should work. I also advice using camel case notation for naming your variables.
The ToLookup function may give you what you need.
cities.ToLookup(c => c.state, c => c.city);
This will create an IGrouping<string, string> where you can iterate through the Key values (states) and operate on a set of city values.
To sort it first, just do cities.OrderBy(c => c.state).ThenBy(c => c.city).
Do the orderby first:
cities.OrderBy(c=>c.cityname).GroupBy (c => c.state);
You might want to order the states to so.
cities.OrderBy(c=>c.cityname).GroupBy (c => c.state).OrderBy (g => g.Key);
This is the query I am trying to do.
var commentActivity = project.ProjectDoc
.Select(c => c.Comment.Select(i => i.UserID))
.Distinct()
.Count();
What I want is the number of comments from distinct users on a specific project, but ANY ProjectDoc. This query "works" the result is just wrong. The model is like this, generically sketched.
Project
ProjectDoc
Comment
Update: I had to go one level deeper, based on the answer below I tried a few things that didn't work so I though I would post this as a reference. Note the two SelectMany methods.
var replyActivity = project.ProjectDoc
.SelectMany(c => c.Comment.SelectMany(r => r.CommentReply.Select(u => u.UserID)))
.Distinct()
.Count();
Use SelectMany instead of Select
project.ProjectDoc
.SelectMany(c => c.Comment.Select(i => i.UserID))
.Distinct()
.Count()
var data = (from con in project.ProjectDoc
select new
{
CommentCount=project.Comment.Count(x=>x.UserID==con.UserID)
}).ToList();
i think this will help you.