My question seems to be something easy, but I can't figure it out.
Let's say I have a "root" IEnumerable of objects. Each object has IEnumerable of strings. How can I obtain a single IEnumerable of those strings?
A possible solution is to do:
public IEnumerable<string> DoExample()
{
foreach (var c in rootSetOfObjects)
{
foreach (var n in c.childSetOfStrings)
{
yield return n;
}
}
}
But maybe there is a magic solution with Linq?
rootSetOfObjects.SelectMany(o => o.childSetOfStrings)
there is SelectMany in Linq that should work for you:
http://msdn.microsoft.com/en-us/vcsharp/aa336758.aspx#SelectManyCompoundfrom1
it definitely works on your collection and compound collections
Related
I have a dictionary of type <MyKey, List<MyClass>> and a List of type MyClass. Now I want to check if there are elements within the latter that are not contained in any of the lists within the dictionary. My first approach was on nesting two loops (one for the actual list, one for the lists within the dictionary). If an item was found I may break the inner loop and continue with next element in outer loop.
foreach (MyClass feature in features)
{
bool found = false;
foreach (var kv in this._features) // this._features is the dictionary
{
if (kv.Value.Contains(feature))
{
found = true;
continue;
}
}
if (!found) result.Add(feature);
}
This works so far, but I´d prefer a shorter approach for this, probably using LINQ. I think it may work if I flatten the values of the dictionary into one single list, but I have no clue on how to achieve this.
Use SelectMany to flatten your values into a IEnumerable<MyClass> then use Except to get differences:
var differentElements = this._features.SelectMany(x => x.Value).Except(features);
result.AddRange(differentElements);
This might not work as expected if MyClass doesn't override Equals and GetHashCode properly.
I have an object that contains a list of child objects, each of which in turn contains a list of children, and so on. Using that first generation of children only, I want to combine all those lists as cleanly and cheaply as possible. I know I can do something like
public List<T> UnifiedListOfTChildren<T>()
{
List<T> newlist = new List<T>();
foreach (childThing in myChildren)
{
newlist = newlist.Concat<T>(childThing.TChildren);
}
return newlist;
}
but is there a more elegant, less expensive LINQ method I'm missing?
EDIT If you've landed at this question the same way I did and are new to SelectMany, I strongly recommend this visual explanation of how to use it. Comes up near the top in google results currently, but is worth skipping straight to.
var newList = myChildren.SelectMany(c => c.TChildren);
Is there a way to remove all items except first one from any type of collection (Control.Items, List ....) using LINQ only ?
No. LINQ is designed for querying collections (no side-effects), not for adding or removing items.
What you can do is write a query that takes the first element of the collection:
var result = source.Take(1);
Note that LINQ doesn't work with all types of collections; you need a LINQ provider to make LINQ work. For instance, source must implement IEnumerable<T> to use the extension methods of the Enumerable Class (LINQ-to-Objects).
How about something using reflection?
static void RemoveButFirst(object o){
Type t = o.GetType();
System.Reflection.MethodInfo rm = t.GetMethod("RemoveAt",
new Type[]{typeof(int)});
System.Reflection.PropertyInfo count = t.GetProperty("Count");
for (int n = (int)(count.GetValue(o,null)) ; n>1; n--)
rm.Invoke(o, new object[]{n-1});
}
This would work any time your collection exposed an int Count property and a RemoveAt(int) method, which I think those collections should.
And a more concise version, using dynamic, if you work with C# 4.0:
public static void RemoveBut(dynamic col, int k){
for (int n = col.Count; n>k; n--)
col.RemoveAt(n-1);
}
You can use .Take(1), but it returns a new collection, and leaves the original intact.
The idea of LINQ came from functional programming where everything is immutable, because of that, they didn't make it possible to modify the collections with LINQ.
Jon Skeet has a comment on the subject: LINQ equivalent of foreach for IEnumerable<T>
How about (in linq):
var result = list.Where(l => l != list.First());
But this would be better:
var result = list.Take(1);
List<string> collection = new List<string>();
collection.RemoveAll(p => p.StartsWith("something"));
listXpto.Where(x=>true /* here goes your query */)
.Select(x=>{listXpto.Remove(x); return null})
But I don´t know the real utility of that.
Remember that the remove method is for ILists, not IQueryable in general.
So, I have a List of objects of class A that contains a List of objects of class B
class A
{
...
List<B> bs;
}
and I have lists:
List<A> mainList;
List<B> listForRemoval;
How can I, using Linq, "clean" mainList, by removing all objects from bs (for every A in mainList) that exists in listForRemoval?
I hope I didn't confuse you with this question. :)
linq itself is probably not a great fit, but you can use some of it's extension methods. Linq typically is mostly for selection, not processing.
mainList.ForEach(x=>x.bs = x.bs.Where(y=>!listForRemoval.Contains(y)).ToList());
Yes, it's possible, as the other answers have shown. I would, however, choose the following solution which does not use LINQ at all:
foreach (var a in mainList) {
a.bs.RemoveAll(b => listForRemoval.Contains(b));
}
Advantages:
It's easier to read and understand.
It's not longer than the LINQ-based solutions---in fact, it's shorter than the accepted, LINQ-based answer.
It removes the elements from bs rather than assigning a new list to bs. This might yield better performance and/or be necessary if the list is used in other places as well.
foreach (var list in mainList) {
list.bs = list.bs.Where(b => !listForRemoval.Contains(b)).ToList();
}
mainList.ForEach(a => a.bs.RemoveAll(b => listForRemoval.Contains(b)));
I'd like to use a for each loop to iterate over two Collections. My first idea was:
foreach (object o in a.Concat(b)) {
o.DoSomething(); }
But the problem is, not all Collections support Concat. So what do to?
Some legacy collection types implement only IEnumerable and not IEnumerable<T>, and therefore don't have the Concat extension method. You can solve this by first using the method Enumerable.Cast<T> and specifying the generic type you want, then it will work with Concat.
Instead of ...
foreach (object o in a.Concat(b)) {
o.DoSomething(); }
Why not just ?
foreach (object o in a) {
o.DoSomething();
}
foreach (object o in b) {
o.DoSomething();
}
If you really want them to be both in the same list, construct a new list and add them together before you start processing.