C# Nested foreach loop optimization - c#

I've got a nested foreach loop that I really need to cut the computation time on. Each collection is at about 50 members, so the extrapolation is huge. I've looked at a lot of information about SelectMany, but I'm still not entirely sure how to use it, or if it's the correct solution.
List<string> StringList; //Populated in previous code
Type[] assemblyTypes = RandomAssembly.GetTypes();
foreach (String name in StringList)
{
foreach (Type at in assemblyTypes)
{
if (name == at.Name)
{
//Do stuff.
}
}
}
Thanks in advance!

Use a lookup (such as a dictionary) to increase the speed of checking for a type name:
List<string> StringList; //Populated in previous code
Dictionary<string,Type> assemblyTypes = RandomAssembly.GetTypes()
.ToDictionary(t => t.Name, t => t);
foreach (String name in StringList)
{
if (assemblyTypes.ContainsKey(name))
{
//Do stuff.
}
}
}
You should also check which of the 2 collections (StringList or assemblyTypes) is likely to be larger. You generally want the larger one to be converted to the lookup in order to reduce the number of iterations.

Load Type[] into a Dictionary or HashSet (depending on your version of .NET) and then the inner loop disappears.
List<string> StringList; //Populated in previous code
Type[] assemblyTypes = RandomAssembly.GetTypes();
Dictionary<String,Type> typesHash = new Dictionary<String,Type>();
foreach ( Type type in assemblyTypes ) {
typesHash.Add( type.Name, type );
}
foreach (String name in StringList) {
Type type = null;
if ( typesHash.TryGetValue( name, out type ) ) {
// do something with type
}
}

You might try something like:
assemblyTypes.Where(x => StringList.Contains(x.Name));
Keep in mind this is case sensitive and ignores whitespace, so you will need to add case consideration or trimming if that's an issue.
Edit: (example for loop usage)
foreach (Type item in assemblyTypes.Where(x => StringList.Contains(x.Name)))
{
// Do stuff
}

If your array/list contains lots of items , you can try using Parallel ForEach loop.

The best optimization might be by querying not for the name but instead for an implemented interface.
Make sure you are optimizing the correct issue/part of your code.

Related

More efficient method to search a list of strings for certain strings?

I have a list of strings. Neither the number of nor the order of these strings is guaranteed. The only thing that is certain is that this list WILL at least contain my 3 strings of interest and inside those strings we'll say "string1", "string2", and "string3" will be contained within them respectively (i.e. these strings can contain more information but those keywords will definitely be in there). I then want to use these results in a function.
My current implementation to solve this is as such:
foreach(var item in myList)
{
if (item.Contains("string1"))
{
myFunction1(item);
}
else if (item.Contains("string2"))
{
myFunction2(item);
}
else if (item.Contains("string3"))
{
myFunction3(item);
}
}
Is there a better way to check string lists and apply functions to those items that match some criteria?
One approach is to use Regex for the fixed list of strings, and check which group is present, like this:
// Note the matching groups around each string
var regex = new Regex("(string1)|(string2)|(string3)");
foreach(var item in myList) {
var match = regex.Match(item);
if (!match.Success) {
continue;
}
if (match.Groups[1].Success) {
myFunction1(item);
}
else if (match.Groups[2].Success)
{
myFunction2(item);
}
else if (match.Groups[3].Success)
{
myFunction3(item);
}
}
This way all three matches would be done with a single pass through the target string.
You could reduce some of the duplicated code in the if statements by creating a Dictionary that maps the strings to their respective functions. (This snippet assumes that myList contains string values, but can easily be adapted to a list of any type.)
Dictionary<string, Action<string>> actions = new Dictionary<string, Action<string>>
{
["string1"] = myFunction1,
["string2"] = myFunction2,
["string3"] = myFunction3
};
foreach (var item in myList)
{
foreach (var action in actions)
{
if (item.Contains(action.Key))
{
action.Value(item);
break;
}
}
}
For a list of only three items, this might not be much of an improvement, but if you have a large list of strings/functions to search for it can make your code much shorter. It also means that adding a new string/function pair is a one-line change. The biggest downside is that the foreach loop is a bit more difficult to read.

In C#, how do I change a Key-Value-Property's value while recursively traversing an ExpandoObject?

The Problem
Using C#, I need to traverse an object that has been cast to an ExpandoObject from XML and replace any "price" property with a new value.
This object is very unstructured and has many layers of nested nodes (nested ExpandoObjects, actually). More specifically, the hierarchy may look like this:
Product => price, quantity, accessories
Each accessory may have a price and quantity and may itself have accessories, this is why I need recursion.
What I have so far
public ExpandoObject UpdatePricing(ExpandoObject exp)
{
//Is there a price property?
var hasPrice = exp.Any(a => a.Key == "price");
if (hasPrice)
{
//update price here
exp.price = 0; //Some other price
}
//Now loop through the whole object. If any of the properties contain an expando, then call this method again
foreach (var kvp in exp)
{
if (kvp.Value is ExpandoObject)
{
//THIS CODE IS NO GOOD BECAUSE kvp.Value has no setter!!
kvp.Value = UpdatePricing(kvp.Value);
}
}
return exp;
}
The problem I run into is that the kvp.Value has no setter, so I can't run this method recursively.
Any suggestions are appreciated. Thanks!
Since ExpandoObject implements IDictionary<string, Object> things can get a bit easier. We can also change the return type to void because we don't need to reassign the result.
void UpdatePrice(ExpandoObject expando, decimal price)
{
var map = (IDictionary<string, Object>)expando;
if (map.ContainsKey("price"))
map["price"] = price;
foreach (var value in map.Values)
{
if (value is ExpandoObject)
UpdatePrice((ExpandoObject)value, price);
}
}
I don't know much about ExpandoObject. But like most dictionary implementations, I assume that in general if you want your key-value pair to be updated to have a different value, you need to go through the dictionary interface.
Note that you (probably) won't be allowed to modify the dictionary while you're enumerating its contents. So you'll need to build a list of elements to update and do that in a separate operation.
For example:
List<string> keysToUpdate = new List<string>();
foreach (var kvp in exp)
{
if (kvp.Value is ExpandoObject)
{
keysToUpdate.Add(kvp.Key);
}
}
foreach (string key in keysToUpdate)
{
exp[key] = UpdatePricing(exp[key]);
}
You could also keep the whole KeyValuePair value in your list, to avoid the second retrieval of the value, but I'm guessing that's not an important optimization here.
I just ran a little test on this and was able to get it to work by having the expando be dynamic:
public static ExpandoObject DoWork(ExpandoObject obj)
{
dynamic expando = obj;
//update price
if (obj.Any(c => c.Key == "price"))
expando.price = 354.11D;
foreach (var item in expando)
{
if (item.Value is ExpandoObject)
{
//call recursively
DoWork(item.Value);
}
}
return expando;
}
it elimitates type safety, but it looks like you don't have that luxury anyways, dynamic is the best way to interact with expandos in fact according to MSDN:
"In C#, to enable late binding for an instance of the ExpandoObject
class, you must use the dynamic keyword. For more information, see
Using Type dynamic (C# Programming Guide)."
this means that if you don't use the dynamic keyword, you are running the Expando in the CLR instead of the DLR which will have some odd consequences like not being able to set values. Hopefully this helps.

What is the for/while equivalent of foreach?

I have a foreach loop that needs converting to a for or while loop. My loop looks like this:
foreach (Item item in Items)
{
// some stuff
}
What is the equivalent for or while loop?
I think I need to use GetEnumerator to get an IEnumerator<Item>, but I don't know how to use it properly. Items isn't a list, otherwise I'd use indexing.
In the simplest case(no disposing etc.) you can use:
var enumerator = Items.GetEnumerator();// `var` to take advantage of more specific return type
while(enumerator.MoveNext())
{
Item item = enumerator.Current;
...
}
For the exact rules check the C# specification 8.8.4 The foreach statement.
A foreach statement of the form
foreach (V v in x) embedded-statement
is then expanded to:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
(Quoted from the C# Language Specification Version 4.0)
The types using here are: "a collection type C, enumerator type E and element type T". E is the return type of GetEnumerator, and not necessarily IEnumerator<V> since foreach uses duck typing. The spec also describes a number of corner cases and how to infer these types, but those details are probably not relevant here.
In C# 5 the declaration of v will be moved into the while loop to get more intuitive closure semantics.
If you're going to use a for loop, it generally means there's some way of quickly accessing the n-th item (usually an indexer).
for(int i = 0; i < Items.Count; i++)
{
Item item = Items[i]; //or Items.Get(i) or whatever method is relevant.
//...
}
If you're just going to access the iterator, you usually just want to use a foreach loop. If, however, you can't, this is usually the model that makes sense:
using(IEnumerator<Item> iterator = Items.GetEnumerator())
while(iterator.MoveNext())
{
Item item = iterator.Current;
//do stuff
}
you could, technically, do this in a for loop, but it would be harder because the construct just doesn't align well with this format. If you were to discuss the reason that you can't use a foreach loop we may be able to help you find the best solution, whether or not that involves using a for loop or not.
This is an equivalent in a for-loop
for (IEnumerator i = Items.GetEnumerator(); i.MoveNext(); )
{
Item item = (Item)i.Current;
// some stuff
}

Iteration bound variable?

This is non-language-specific, but I'll use examples in C#. Often I face the problem in which I need to add a parameter to an object inside any given iteration of at least one of its parameters, and I have always to come up with a lame temporary list or array of some kind concomitant with the problem of keeping it properly correlated.
So, please bear with me on the examples below:
Is there an easier and better way to do this in C sharp?
List<String> storeStr;
void AssignStringListWithNewUniqueStr (List<String> aList) {
foreach (String str in aList) {
storeStr.add(str);
str = AProcedureToGenerateNewUniqueStr();
}
}
void PrintStringListWithNewUniqueStr (List<String> aList) {
int i = 0;
foreach (String str in aList) {
print(str + storeStr[i]);
i++;
}
}
Notice the correlation above is guaranteed only because I'm iterating through an unchanged aList. When asking about a "easier and better way" I mean it should also make sure the storeStr would always be correlated with its equivalent on aList while keeping it as short and simple as possible. The List could also have been any kind of array or object.
Is there any language in which something like this is possible? It must give same results than above.
IterationBound<String> storeStr;
void AssignStringListWithNewUniqueStr (List<String> aList) {
foreach (String str in aList) {
storeStr = str;
str = AProcedureToGenerateNewUniqueStr();
}
}
void PrintStringListWithNewUniqueStr (List<String> aList) {
foreach (String str in aList) {
print(str + storeStr);
}
}
In this case, the fictitious "IterationBound" kind would guarantee the correlation between the list and the new parameter (in a way, just like Garbage Collectors guarantee allocs). It would somehow notice it was created inside an iteration and associate itself with that specific index (no matter if the syntax there would be uglier, of course). Then, when its called back again in another iteration and it was already created or stored in that specific index, it would retrieve this specific value of that iteration.
Why not simply project your enumerable into a new form?
var combination = aList
.Select(x => new { Initial = x, Addition = AProcedureToGenerateNewUniqueStr() })
.ToList()
.ForEach(x =>
{
print(x.Initial + x.Addition);
});
This way you keep each element associated with the new data.
aList.ForEach(x => print(x + AProcedureToGeneratorNewUniqueString()));

Is it possible for a 'foreach' loop to have a condition check?

If I have a foreach loop, is there any way to check a boolean as well?
I don't want to check once inside the foreach() and then break for example. I want to foreach over a collection and at the same time evaluate if something is true.
For example, I don't want to do:
IEnumerable<Job> jobs = currentJobs;
foreach(Job job in jobs)
{
if (found)
break;
}
I found another approach:
foreach (var car in cars) if (!rentedCars.Contains(car))
{
// Magic
}
Try using TakeWhile.
From the example:
string[] fruits = { "apple", "banana", "mango", "orange",
"passionfruit", "grape" };
IEnumerable<string> query =
fruits.TakeWhile(fruit => String.Compare("orange", fruit, true) != 0);
foreach (string fruit in query)
{
Console.WriteLine(fruit);
}
/*
This code produces the following output:
apple
banana
mango
*/
You could always turn it into a for loop.
for (i = 0; i < jobs.Count && booleanTrue; i++) {
// do a lot of great stuff
}
You would also need to change jobs from IEnumerable to IList. I think IList would serve your purposes better. IEnumerable lazy evaluates the elements just before you need them and doesn't include the associated collection helper methods.
Not hugely loving it, but maybe some LINQ?
bool yourBool = false;
foreach(var item in
collection.TakeWhile(x => yourBool))
{...}
?
Am I understanding correctly that you
have a sequence of elements
want to take some action with each element
but you want to break on the first element you encounter for which some condition is true?
I don't understand the resistance to using foreach loops; I would either stick with what you have or
foreach(var job in jobs.TakeWhile(x => someCondition(x)) {
someAction(job);
}
No, a foreach simply works for each element.
You can combine multiple conditions in a regular for(a; b; c) loop. Like for(a; b && x; c)

Categories

Resources