Nested foreach - Need to operate on object at each level - c#

I was trying to refactor some nested foreach loops but ran into an issue. This is the original code:
foreach(var doc in customTrackerDocuments)
{
foreach(var rule in doc.Rules)
{
foreach(var eval in rule.Evaluations)
{
// Do something with customTrackerDocuments, rules, and evaluations
// doc, rule, and eval are all available here
}
}
}
It's admittedly clean and straightforward, so maybe it should just stay like that. However, I've always tried to reduce complexity and increase readability, so I tried this:
foreach(var eval in customTrackerDocuments.SelectMany(doc => doc.Rules).SelectMany(rule => rule.Evaluations))
{
// Do something with customTrackerDocuments, rules, and evaluations
// doc and rule are NOT available here
}
The issue is that doc and rule are no longer available to use in the loop. Is there a way to have them be available with this approach? Or should I just use the first option that has the three nested loops?
I have a fiddle here: https://dotnetfiddle.net/oBMfQC

They are not available because you're only projecting rule.Evaluations in your final SelectMany. You could build up an anonymous type:
foreach(var eval in customTrackerDocuments.SelectMany(doc => doc.Rules, rule => new {doc, rule})
.SelectMany(docrule => docrule.rule.Evaluations, eval => new {docrule.doc, docrule.rule, eval}))
{
// eval.doc, eval,rule, eval.eval are available here
}
Whether or not that's more readable or less complex is debatable. It certainly won't be any faster or use less memory.

Related

Converting foreach to Linq

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

Using Where and ForEach to modify specific elements in a list

If I have a list of objects that have the properties fruitName and numFruits and I want to pluralize the fruitName where numFruits is greater than 1, is it possible to do that in a single statement by chaining together Where and Foreach?
Something like:
fruitList.Where(fl => fl.numFruits > 1).ForEach(fl => fl.fruitName = fl.fruitName + "s");
I tried the above and it doesn't work. It complains that System.Collections.Generic.IEnumerable doesn't contain a definition for ForEach.
Typically you want to use foreach the language construct when possible. Eric Lippert has a blog post going into additional detail as to why.
Loops are good when you are doing modifications as it makes finding those modifications easier.
foreach (var fl in fruitList.Where(fl => fl.numFruits > 1))
{
fl.fruitName = fl.fruitName + "s";
}
Is more straightforward and accomplishes the same task.
If you really want a one-liner (it will be harder to maintain) and want to keep the original list intact but only modify some of the elements, you'll have to use a full anonymous function. If you need multiple statements (a block of code), you'll need to include the braces and statement-terminating semicolons like below:
fruitList.ForEach(fl => { fl.fruitName = fl.numFruits > 1 ? fl.fruitName + "s" : fl.fruitName; });
This works on the original list (no subset) and does basically the exact same thing a structured foreach would do.
There's a good blog post by Eric Lippert on why there is no “ForEach” sequence operator extension method, essentially the reason is:
The first reason is that doing so violates the functional programming
principles that all the other sequence operators are based upon.
Clearly the sole purpose of a call to this method is to cause side
effects. The purpose of an expression is to compute a value, not to
cause a side effect. The purpose of a statement is to cause a side
effect. The call site of this thing would look an awful lot like an
expression (though, admittedly, since the method is void-returning,
the expression could only be used in a “statement expression”
context.) It does not sit well with me to make the one and only
sequence operator that is only useful for its side effects.
If you wanted to do this in a single statement you could use a .Select()
var newFruitList = fruitList.Where(fl => fl.numFruits > 1).Select(fl => fl.fruitName + "s");
Like #Tim Schmelter suggested, you can use ToList() to convert to a list and then use the ForEach method on the result returned. Although the ToList() might return a shorter list based on the filter, the original objects themselves would be changed and your fruitList will remain unchanged.
fruitList.Where(fl => fl.numFruits > 1).ToList().ForEach(fl => fl.fruitName = fl.fruitName + "s");
// fruitList still has all elements
You can use the static Array.ForEach method to update the list.
Array.ForEach(fruitList.Where(fl => fl.numFruits > 1).ToArray(), x => { x.fruitName += "s"; });
Given that "append an s" doesn't actually give you the correct answer for many fruits, any approach that does that will give you an incorrect answer, no matter how well it does it.
Consider using a lookup table to map singlular to plurals (and vice versa) instead:
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
private static Dictionary<string, string> fruitLookup =
new Dictionary<string, string>
{
{"blueberry", "blueberries"},
{"peach", "peaches"},
{"apple", "apples"}
};
public static void Main()
{
var fruitList = new List<string> {"blueberry", "peach", "apple"};
// Here is your one-line conversion:
var plurals = fruitList.Select(f => fruitLookup[f]).ToList();
foreach (var p in plurals)
{
Console.WriteLine(p);
}
}
}

Replace nested foreach to Increase performance

I have the following code which take longer time to execute. Is there any alternative that I can replace below code with LINQ or any other way to increase the performance?
var usedLists = new HashSet<long>();
foreach (var test in Tests)
{
var requiredLists = this.GetLists(test.test, selectedTag);
foreach (var List in requiredLists)
{
if (!usedLists.Contains(List.Id))
{
usedLists.Add(List.Id);
var toRecipients = List.RecepientsTo;
var ccRecipients = List.RecipientsCC;
var bccRecipients = List.RecipientsBCC;
var replyTo = new List<string>() { List.ReplyTo };
var mailMode = isPreviewMode ? MailMode.Display : MailMode.Drafts;
OutlookModel.Instance.CreateEmail(toRecipients, ccRecipients, bccRecipients, this.Draft, mailMode, replyTo);
}
}
}
What do you actually do?
Looking at
foreach (var test in Tests)
{
var requiredLists = this.GetLists(test.test, selectedTag);
foreach (var List in requiredLists)
{
if (!usedLists.Contains(List.Id))
it looks to me that you try to get unique "List"s (with all those "var" I cannot tell the actual type). So you actually could replace that with a new function (to be written by you) and do
var uniqueLists = this.GetUniqueLists(Tests, selectedTag);
and finally call
this.Sendmails(uniqueLists);
which would iterate thru that list and send the mails.
If that could speed up the code or not, depends severely on the underlying GetLists / GetUniqueLists functions.
But in any case, it would make a great progress to your code: it becomes readable and testable.
Assuming the result from GetList is large/slow enough in comparison to CreateEmail call, skipping your contains-add hashset comparison will speed things up. Try a call to Distinct([Your ListComparer]) or GroupBy():
var requiredLists =
Tests.SelectMany(test => this.GetLists(test.test, selectedTag))
.Distinct([Your ListComparer]);
// or
var requiredLists =
Tests.SelectMany(test => this.GetLists(test.test, selectedTag))
.GroupBy(x => x.Id).SelectMany(x => x.First());
foreach (var List in requiredLists)
{
// (...)
OutlookModel.Instance.CreateEmail(toRecipients, ccRecipients, bccRecipients, this.Draft, mailMode, replyTo);
}
You could also try PLINQ to speed up things but I think you might run into trouble with the CreateEmail call, as it's probably a COM object, so multithreading will be a hassle. If GetList is slow enough to be worth the multithread overhead, you may experiment with it in the first call when creating requiredLists.
It might be able to done with the BackgroundWorker class or something similar.
A very rough outline would be to wrap the create email arguments into a separate class, then asynchronously start a background task while the loop continues processing the list.
Edit
The Task class might be of use too. This is assuming the CreateEmail function is the one that's taking up all the time. If it's the loop that's taking all the time then there's no point in going this route though.
Edit
Looking at the algorithmic complexity, the GetLists() calls run in O(n) time, but the CreateEmail() calls run in O(n^2) time, so - all other things being equal - the code block containing the CreateEmail() call would be a better candidate to optimise first.
Each foreach loop creates an Enumerator object, so using a regular for-loop might speed it up a little, but this might be a very minor improvement.
Like stormenet said, the CreateEmail function could be the bottleneck. You might want to make that asynchronous.

In a generic list, is there a way to copy one property to another in a declarative /LINQ manner?

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.

Apply function to all elements of collection through LINQ [duplicate]

This question already has answers here:
LINQ equivalent of foreach for IEnumerable<T>
(22 answers)
Closed 6 years ago.
I have recently started off with LINQ and its amazing. I was wondering if LINQ would allow me to apply a function - any function - to all the elements of a collection, without using foreach. Something like python lambda functions.
For example if I have a int list, Can I add a constant to every element using LINQ
If i have a DB table, can i set a field for all records using LINQ.
I am using C#
A common way to approach this is to add your own ForEach generic method on IEnumerable<T>. Here's the one we've got in MoreLINQ:
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
source.ThrowIfNull("source");
action.ThrowIfNull("action");
foreach (T element in source)
{
action(element);
}
}
(Where ThrowIfNull is an extension method on any reference type, which does the obvious thing.)
It'll be interesting to see if this is part of .NET 4.0. It goes against the functional style of LINQ, but there's no doubt that a lot of people find it useful.
Once you've got that, you can write things like:
people.Where(person => person.Age < 21)
.ForEach(person => person.EjectFromBar());
The idiomatic way to do this with LINQ is to process the collection and return a new collection mapped in the fashion you want. For example, to add a constant to every element, you'd want something like
var newNumbers = oldNumbers.Select(i => i + 8);
Doing this in a functional way instead of mutating the state of your existing collection frequently helps you separate distinct operations in a way that's both easier to read and easier for the compiler to reason about.
If you're in a situation where you actually want to apply an action to every element of a collection (an action with side effects that are unrelated to the actual contents of the collection) that's not really what LINQ is best suited for, although you could fake it with Select (or write your own IEnumerable extension method, as many people have.) It's probably best to stick with a foreach loop in that case.
You could also consider going parallel, especially if you don't care about the sequence and more about getting something done for each item:
SomeIEnumerable<T>.AsParallel().ForAll( Action<T> / Delegate / Lambda )
For example:
var numbers = new[] { 1, 2, 3, 4, 5 };
numbers.AsParallel().ForAll( Console.WriteLine );
HTH.
haha, man, I just asked this question a few hours ago (kind of)...try this:
example:
someIntList.ForEach(i=>i+5);
ForEach() is one of the built in .NET methods
This will modify the list, as opposed to returning a new one.
Or you can hack it up.
Items.All(p => { p.IsAwesome = true; return true; });
For collections that do not support ForEach you can use static ForEach method in Parallel class:
var options = new ParallelOptions() { MaxDegreeOfParallelism = 1 };
Parallel.ForEach(_your_collection_, options, x => x._Your_Method_());
You can try something like
var foo = (from fooItems in context.footable select fooItems.fooID + 1);
Returns a list of id's +1, you can do the same with using a function to whatever you have in the select clause.
Update: As suggested from Jon Skeet this is a better version of the snippet of code I just posted:
var foo = context.footable.Select(foo => foo.fooID + 1);
I found some way to perform in on dictionary contain my custom class methods
foreach (var item in this.Values.Where(p => p.IsActive == false))
item.Refresh();
Where 'this' derived from : Dictionary<string, MyCustomClass>
class MyCustomClass
{
public void Refresh(){}
}

Categories

Resources