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)));
Related
Current Code:
For each element in the MapEntryTable, check the properties IsDisplayedColumn and IsReturnColumn and if they are true then add the element to another set of lists, its running time would be O(n), there would be many elements with both properties as false, so will not get added to any of the lists in the loop.
foreach (var mapEntry in MapEntryTable)
{
if (mapEntry.IsDisplayedColumn)
Type1.DisplayColumnId.Add(mapEntry.OutputColumnId);
if (mapEntry.IsReturnColumn)
Type1.ReturnColumnId.Add(mapEntry.OutputColumnId);
}
Following is the Linq version of doing the same:
MapEntryTable.Where(x => x.IsDisplayedColumn == true).ToList().ForEach(mapEntry => Type1.DisplayColumnId.Add(mapEntry.OutputColumnId));
MapEntryTable.Where(x => x.IsReturnColumn == true).ToList().ForEach(mapEntry => Type1.ReturnColumnId.Add(mapEntry.OutputColumnId));
I am converting all such foreach code to linq, as I am learning it, but my question is:
Do I get any advantage of Linq conversion in this case or is it a disadvantage ?
Is there a better way to do the same using Linq
UPDATE:
Consider the condition where out of 1000 elements in the list 80% have both properties false, then does where provides me a benefit of quickly finding elements with a given condition.
Type1 is a custom type with set of List<int> structures, DisplayColumnId and ReturnColumnId
ForEach ins't a LINQ method. It's a method of List. And not only is it not a part of LINQ, it's very much against the very values and patterns of LINQ. Eric Lippet explains this in a blog post that was written when he was a principle developer on the C# compiler team.
Your "LINQ" approach also:
Completely unnecessarily copies all of the items to be added into a list, which is both wasteful in time and memory and also conflicts with LINQ's goals of deferred execution when executing queries.
Isn't actually a query with the exception of the Where operator. You're acting on the items in the query, rather than performing a query. LINQ is a querying tool, not a tool for manipulating data sets.
You're iterating the source sequence twice. This may or may not be a problem, depending on what the source sequence actually is and what the costs of iterating it are.
A solution that uses LINQ as much as is it is designed for would be to use it like so:
foreach (var mapEntry in MapEntryTable.Where(entry => mapEntry.IsDisplayedColumn))
list1.DisplayColumnId.Add(mapEntry.OutputColumnId);
foreach (var mapEntry in MapEntryTable.Where(entry => mapEntry.IsReturnColumn))
list2.ReturnColumnId.Add(mapEntry.OutputColumnId);
I would say stick with the original way with the foreach loop, since you are only iterating through the list 1 time over.
also your linq should look more like this:
list1.DisplayColumnId.AddRange(MapEntryTable.Where(x => x.IsDisplayedColumn).Select(mapEntry => mapEntry.OutputColumnId));
list2.ReturnColumnId.AddRange(MapEntryTable.Where(x => x.IsReturnColumn).Select(mapEntry => mapEntry.OutputColumnId));
The performance of foreach vs Linq ForEach are almost exactly the same, within nano seconds of each other. Assuming you have the same internal logic in the loop in both versions when testing.
However a for loop, outperforms both by a LARGE margin. for(int i; i < count; ++i) is much faster than both. Because a for loop doesn't rely on an IEnumerable implementation (overhead). The for loop compiles to x86 register index/jump code. It maintains an incrementor, and then it's up to you to retrieve the item by it's index in the loop.
Using a Linq ForEach loop though does have a big disadvantage. You cannot break out of the loop. If you need to do that you have to maintain a boolean like "breakLoop = false", set it to true, and have each recursive exit if breakLoop is true... Bad performing there. Secondly you cannot use continue, instead you use "return".
I never use Linq's foreach loop.
If you are dealing with linq, e.g.
List<Thing> things = .....;
var oldThings = things.Where(p.DateTime.Year < DateTime.Now.Year);
That internally will foreach with linq and give you back only the items with a year less than the current year. Cool..
But if I am doing this:
List<Thing> things = new List<Thing>();
foreach(XElement node in Results) {
things.Add(new Thing(node));
}
I don't need to use a linq for each loop. Even if I did...
foreach(var node in thingNodes.Where(p => p.NodeType == "Thing") {
if (node.Ignore) {
continue;
}
thing.Add(node);
}
even though I could write that cleaner like
foreach(var node in thingNodes.Where(p => p.NodeType == "Thing" && !node.Ignore) {
thing.Add(node);
}
There is no real reason I can think of to do this..>
things.ForEach(thing => {
//do something
//can't break
//can't continue
return; //<- continue
});
And if I want the fastest loop possible,
for (int i = 0; i < things.Count; ++i) {
var thing = things[i];
//do something
}
Will be faster.
Your LINQ isn't quite right as you're converting the results of Where to a List and then pseudo-iterating over those results with ForEach to add to another list. Use ToList or AddRange for converting or adding sequences to lists.
Example, where overwriting list1 (if it were actually a List<T>):
list1 = MapEntryTable.Where(x => x.IsDisplayedColumn == true)
.Select(mapEntry => mapEntry.OutputColumnId).ToList();
or to append:
list1.AddRange(MapEntryTable.Where(x => x.IsDisplayedColumn == true)
.Select(mapEntry => mapEntry.OutputColumnId));
In C#, to do what you want functionally in one call, you have to write your own partition method. If you are open to using F#, you can use List.Partition<'T>
https://msdn.microsoft.com/en-us/library/ee353782.aspx
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);
I have a data structure like
public DespatchGroup(DateTime despatchDate, List<Products> products);
And I am trying to do...
var list = new List<DespatchGroup>();
foreach (var group in dc.GetDespatchedProducts().GroupBy(i => i.DespatchDate))
{
// group.Values is not correct... how do I write this?
list.Add(new DespatchGroup(group.Key, group.Values);
}
I'm obviously not understanding IGrouping as I can't see how to actually get to the data records within the group!
The group implements IEnumerable<T> - In the general case, just call foreach over the group. In this case, since you need a List<T>:
list.Add(new DespatchGroup(group.Key, group.ToList());
There's no Values property or similar because the IGrouping<T> itself is the IEnumerable<T> sequence of values. All you need to do in this case is convert that sequence to a list:
list.Add(new DespatchGroup(group.Key, group.ToList());
For any selected group,you could call
var selectedGroupValues=selectedGroup.SelectMany(x=>x);
Just a related tip - since, as the other answers have said, the grouping is an IEnumerable, if you need to access a specific index you can use group.ElementAt(i).
This is probably obvious to a lot of people but hopefully it will help a few!
I have a class with two properties, say
public class Book {
public string TitleSource { get; set; }
public string TitleTarget { get; set; }
}
I have an IList<Book> where the TitleTarget is null and for each item in the list, I need to copy the TitleSource property to the TitleTarget property. I could do this through a loop, sure, but it seems like there's a LINQ or nice declarative way to do this. Is there?
Linq was designed as a way to consume things. If you look at web discussions about why there is no IEnumerable.ForEach(...) extension, you'll see that the Linq designers purposefully avoided Linq to Object scenarios where the methods were designed to change object values.
That said, you can cheat by "selecting" values and not using the results. But, that creates items which are thrown away. So, a foreach loop is much more efficient.
Edit for people who really want something besides foreach
Another "cheat" that wouldn't produce a new list would be to use a method that does little work of it's own, like Aggregate, All, or Any.
// Return true so All will go through the whole list.
books.All(book => { book.TitleTarget = book.TitleSource; return true; });
It's not LINQ as such, but there's:
books.Where(book => book.TitleTarget == null).ToList()
.ForEach(book => book.TitleTarget = book.TitleSource);
The main point is the ToList method call: there's no ForEach extension method (I don't think?) but there is one on List<T> directly. It wouldn't be hard to write your own ForEach extension method as well.
As to whether this would be better than a simple foreach loop, I'm not so sure. I would personally choose the foreach loop, since it makes the intention (that you want to modify the collection) a bit clearer.
#John Fisher is correct, there is no IEnumerable.ForEach.
There is however a ForEach on List<T>. So you could do the following:
List<Book> books = GetBooks();
books.ForEach(b => b.TitleTarget = b.TitleSource);
If you wanted a IEnumerable.ForEach it would be easy to create one:
public static class LinqExtensions
{
public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
foreach (var item in source)
{
action(item);
}
}
}
You can then use the following snippet to perform your action across your collection:
IList<Book> books = GetBooks();
books.ForEach(b => b.TitleTarget = b.TitleSource);
If you can use .NET 4.0, and you are using a thread-safe collection then you can use the new parallel ForEach construct:
using System.Threading.Tasks;
...
Parallel.ForEach(
books.Where(book => book.TitleTarget == null),
book => book.TitleTarget = book.TitleSource);
This will queue tasks to be run on the thread pool - one task that will execute the assignment delegate for each book in the collection.
For large data sets this may give a performance boost, but for smaller sets may actually be slower, given the overhead of managing the thread synchronization.
books.Select(b => b.TitleTarget = b.TitleSource);
This doesn't create any 'new items', just a query that you won't enumerate. That doesn't seem like a big deal to me.
list vclAsset<FullAsset>
list callsigns<string>
foreach(FullAsset fa in vclAsset)
{
if (callsigns.contains(fa.asset.callsign))
{
//do something
}
}
Is there a more elegant way to do the above? A FullAsset object contains an Asset object which in turn has a string "Callsign." Each callsign will be unique, so my list callsigns will only have one of each string, and no two FullAsset objects will share an Asset.callsign variable.
In a nutshell I want to pull all the FullAssets that have a certain callsign, but using a foreach seems clumsy (given that the number of FullAssets that could be contained in said list potentially has no upper limit).
You could use a lambda expression, something like this:
foreach(FullAsset fa in vclAsset.Where(a => callsigns.contains(a.asset.callsign))
{
// do something
}
If your keys are unique, you can use a Dictionary or a Hashtable to speed up searching.
If you only want to find a certain item, you can use the List<T>.Find method and supply a predicate.
FullAsset result = vclAsset.Find
(fa => callsigns.contains(fa.asset.callsign));
or
List<FullAsset> results = vclAsset.FindAll
(fa => callsigns.contains(fa.asset.callsign));
If you are using .Net 3.5, LINQ Where may be a better solution, as others have stated, since it returns an enumerator (lazy evaluation) vs a full List.
Sure, using linq.
var assets= vclAsset.Where(fullA=>allsigns.contains(fullA.asset.callsign));
assets will be some enumerable structure.
I can recommend 100 Linq samples for inspiration and learning
Not sure if it counts as more elegant but you can use linq...
var results = from fa in vclAsset
where callsigns.Contains(fa.asset.callsign)
select fa;
var result = vclAsset.Where(x=>callsigns.Any(y=>x.asset.callsign==y));
P.s. I would rename vclAsset and asset/callsign properties.
You can also use the Join function to do this.
var sortedList = vclAsset.Join(callsigns,
x => x.asset.callsign, x => x,
x, y => x);
This is the list of vclAssets that have the listed callsign.