Linq Query sub-selection issue - c#

The objects are WPF specific, but same thing...
var v = Style.Triggers.Where(x => x is EventTrigger)
.Cast<EventTrigger>()
.Select(x => x.Actions);
At this point, I get 3 TriggerActionCollections which is correct. What I want to do next is select the items within each collection that are "is BeginStoryboard". I can't seem to work out how to select the items within Actions (the TriggerActionsCollection).
I was thinking something like this:
var v = Style.Triggers.Where(x => x is EventTrigger)
.Cast<EventTrigger>()
.Select(x => x.Actions.Select(y => y).Where(y => y is BeginStoryboard));
But that isn't working. Any help guys?
For those non-wpf folks. Yes, there are 3 TriggerActionCollections and in one of those there is a BeginStoryBoard object. But for arguments sake I want EVERY BeginStoryBoard object flattened out.

Have you tried
var v = Style.Triggers.Where(x => x is EventTrigger)
.Cast<EventTrigger>()
.SelectMany(x => x.Actions)
.Where(...)
SelectMany instead of Select returns single collection instead of collection of collections.

var v = Style.Triggers
.OfType<EventTrigger>()
.SelectMany(x => x.Actions)
.OfType<BeginStoryboard>();

Related

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

C# LINQ getting duplicates from a list

I have a list of objects that have a string, and int and another int.
I want to be able to create a list of all the objects that have a duplicate string.
Here is what I have so far:
MyObject duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
The error I am getting is that I cannot implicitly convert the type System.Collections.Generic.List<string, MyObject> to MyObject
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g=>g)
.ToList();
you need to write
List<MyObject> duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
You could use ToLookup to make a nice data structure with all the info you need
var objectsByString = allMyObjects.ToLookup(o => o.MyString);
This will return a Lookup<string, MyObject>. You can get the duplicate strings using:
var duplicateStrings = objectsByString.Where(l => l.Count()>1).Select(l => l.Key);
which will return a IEnumerable<string> with the duplicate strings. And, for each duplicate you can access the actual objects that have duplicates using something like this:
string duplicateKey = duplicateStrings.First();
var duplicateObjects = objectsByString[duplicateKey]
which returns a IEnumerable<MyObject> with the items that have that string.
There are several problem, the first is a List-of-MyObject cannot be assigned to MyObject, so let's use var to ignore this for a second.
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.ToList();
Now, the type of duplicates is List<IGrouping<string, MyObject>> (despite the incorrectly reported error message). Whoops, gotta get rid of (or write to code to account for) the groups!
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g => g)
.ToList();
Now the type of duplicates is List<MyObject>, after having selected every ("selected many") object from every group with more than one item. Better, but this still isn't an MyObject. Well, that's fine: fix the declared type of the variable (that var was previously automatically doing)..
List<MyObject> duplicates = /* same as before */;
Or leave var to do it's thing and if an IEnumerable<MyObject> is fine, simply omit the ToList:
var duplicates = allMyObjects.GroupBy(a => a.MyString)
.Where(a => a.Count() > 1)
.SelectMany(g => g);
Go forth and iterate thy duplicates!

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

Linq using Distinct() in C# Lambda Expression

SFC.OrderFormModifiedMonitoringRecords
.SelectMany(q => q.TimeModify, w => w.DateModify)
.Distinct()
.OrderBy(t => t)
.SelectMany(t => new { RowID = t.rowID, OFnum = t.OFNo });
It's Error did i missed something or is it Completely Coded The Wrong Way? After this i'm gonna use this on a Foreach method to gather up multiple data without the duplicates.
The delegate you pass to SelectMany must return an IEnumerable and is for collapsing multiple collections into one. So yes, something's definitely wrong here. I think you've confused it with Select which simply maps one collection to another.
Without knowing what your goal is, it's hard to know exactly how to fix it, but I'm guessing you want something like this:
SFC.OrderFormModifiedMonitoringRecords
.OrderBy(t => t.DateModify)
.ThenBy(t => t.TimeModify)
.Select(t => new { RowID = t.rowID, OFnum = t.OFNo })
.Distinct();
Or in query syntax:
(from t in SFC.OrderFormModifiedMonitoringRecords
orderby t.DateModify, t.TimeModify
select new { RowID = t.rowID, OFnum = t.OFNo })
.Distinct();
This will order the records by DateModify then by TimeModify, select two properties, rowID and OFNo and return only distinct pairs of values.

Remove duplicates of a List, selecting by a property value in C#?

I have a list of objects that I need some duplicates removed from. We consider them duplicates if they have the same Id and prefer the one whose booleanValue is false. Here's what I have so far:
objects.GroupBy(x => x.Id).Select(x => x.Where(y => !y.booleanValue));
I've determined that GroupBy is doing no such grouping, so I don't see if any of the other functions are working. Any ideas on this? Thanks in advance.
You can do this:
var results =
from x in objects
group x by x.Id into g
select g.OrderBy(y => y.booleanValue).First();
For every Id it finds in objects, it will select the first element where booleanValue == false, or the the first one (if none of them have booleanValue == false).
If you prefer fluent syntax:
var results = objects.GroupBy(x => x.Id)
.Select(g => g.OrderBy(y => y.booleanValue).First());
Something like this should work:
var result =
objects.GroupBy(x => x.Id).Select(g =>
g.FirstOrDefault(y => !y.booleanValue) ?? g.First())
This assumes that your objects are of a reference type.
Another possibility might be to use Distinct() with a custom IEqualityComparer<>.
This partially answers the question above, but I justed need a really basic solution:
objects.GroupBy(x => x.Id)
.Select(x => x.First())
.ToArray();
The key to getting the original object from the GroupBy() is the Select() getting the First() and the ToArray() gets you an array of your objects, not a Linq object.

Categories

Resources