How to improve foreach within foreach - c#

I have a foreach loop within foreach loop which looks like this:
// Item is an abstract class. Item1, Item2, etc. are his heirs.
List<Item> allItems = new List<Item> { new Item1(), new Item2(), new Item3(), new Item4() };
List<Type> affectedItems = new List<Type> { typeof(Item1), typeof(Item3) };
foreach(Item i in allItems)
foreach(Type t in affectedItems)
if(i.GetType().Equals(t))
{
// does something
}
How can I improve my code so inner loop would not waste so much time checking for nonexistent items in a list?

Any linq extension method you use (eg Where, Any) is an extra loop.
You need to minimize the number of loops (especially the nested ones), in your case the best way is to use a fast lookup data structure:
List<Item> allItems = new List<Item>{ new Item1(), new Item2(), new Item3(), new Item4() };
HashSet<Type> affectedItems = new HashSet<Type>(){ typeof(Item1), typeof(Item3) };
foreach (Item i in allItems)
{
if (affectedItems.Contains(i.GetType()))
{
// Do Something
}
}
This is the fastest approach so far with minimal loops and takes up to 0.02 milliseconds, while other methods take up to 0.7.
A quick advise, whenever you have Loop up process and you want to optimize look for data structures to use like HashSet, Dictionary, Lookup, etc..

Write single loop like this:
foreach (Item i in allItems.Where (item => affectedItems.
Any (type => type.Equals (item.GetType ()))))
This will return only items from allItems that are type from affectedItems.
The only difference from your code is that Any will stop searching after first type is found. That's the only improvement. But you can add break in your if statement. Then these two code snippets would be equivalent.

If you have a large list of types then changing your list to HashSet could help in performance otherwise it doesn't make much difference.
List<Item> allItems = new List<Item> { new Item1(), new Item2(), new Item3(), new Item4() };
HashSet<Type> affectedItems = new HashSet<Type> { typeof(Item1), typeof(Item3) };
var items = allItems.Where(item=> affectedItems.Contains(item.GetType()));
foreach(var item in items)
{
// does something
}

This one was the quickest (I try your code, Linq with intersect and this one):
Array.ForEach(allItems.ToArray(), i => {
var t = i.GetType();
if (affectedItems.Contains(t)) {
// Do Something
}
});

Related

How can I add an item to a list and return a new list

I was asked this question today:
How can I add an item to a list and return that list back?
The code for List<T>.Add(T) returns void. So you can't do something like this:
var list = new List<string>{"item1","item2"};
var newList = list.Add("item3");
This is related to using AutoMapper, although that part isn't particularly important.
One option is Linq, with Concat:
var list = new List<string>{"item1", "item2"};
var newList = list.Concat(new[] { "item3" }).ToList();
In typical Linq fashion, list stays the same, and newList contains all the items from list as well as the items in the new list, in this case just "item3".
You can skip the .ToList() to keep the IEnumerable<string> result if that fits your use case.
If you find yourself doing this often with individual items, you can use something like this extension method to pass them without the new[] { ... } syntax:
public static IEnumerable<T> ConcatItems<T>(this IEnumerable<T> source, params T[] items)
{
return source.Concat(items);
}
Because of the params array the earlier example becomes:
var list = new List<string>{"item1", "item2"};
var newList = list.ConcatItems("item3").ToList();
Make sure not to mix this up with Union, which removes duplicate items. (Searching for those duplicates is overhead that you probably don't want!)
The answer to this question was relatively simple:
var list = new List<string>(new List<string>{"item1","item2"}){"item3"};
List<T>() has a constructor that can take in IEnumerable<T> (MSDN). Additionally, you can use the object setter to put new items into the list.
So, for a more complicated example:
var originalList = new List<string>();
originalList.Add("item1");
originalList.Add("item2");
var newList = new List<string>(originalList){"item3"};
You can simply do :
List<string> list = new List<string>{"item1","item2"};
List<string> newList = null;
(newList = list.ToList()).Add("item3");
Or create your own extension method :
public static class Helper
{
public static List<T> MyAdd<T>(this List<T> collection, T item)
{
collection.Add(item);
return collection;
}
}
And use it :
List<string> list = new List<string>{"item1","item2"};
List<string> newList = list.MyAdd("item3"); // same object though
List<string> newList2 = newList.ToList().MyAdd("item4").MyAdd("item5"); // different object
One property of an ImmutableList<T> (and other similar data structures from System.Collections.Immutable) is that it doesn't mutate the original list, it returns another immutable list with the added value.
So doing this:
var originalImmutable = ImmutableList<int>.Create(1, 2);
var otherImmutable = originalImmutable.Add(3);
Will result in a shallow copied new list each time you call Add.
The most readable and maintainable solution is to copy the list and then add the item:
var list = new List<string>{"item1","item2"};
var newList = list.toList();
newList.Add("item3");
Seven years have passed since the question has been asked but Enumerable class now offers Prepend and Append methods that could be used in a straightforward fashion:
var list = new List<string>{"item1","item2"};
var newList = list.Append("item3").ToList();

Comparing two lists of nested lists and returning the added/changed/removed items

I've looked at many similar questions on stackoverflow, but I haven't seen an exact match for my problem.
I need to compare two "lists of nested lists" and capture the differences. One is an "old" list and the other is a "new" list. When comparing the nested lists, they can be considered equal if all of the NESTED list items (the MyObject.Ids) are present in both lists in order (you can assume that the nested MyObject.Ids lists are already sorted and that there are no duplicates). The MyObject.Id and MyObject.Name properties are not considering in the equality comparison, but they are still important metadata for MyObject's which should not get lost.
I am not looking for a boolean indicator of equality. Instead I need to create three new lists which capture the differences between the old and new lists (e.g. a list of items which were Added, a list of items which were Removed, and a list of items which were present in both lists).
Below is an example of some code which does exactly what I want! What I would like to know is how to make this shorter/better/simpler (cutting out one of the for loops would be a good start). To make things trickier, please assume that you cannot make any changes to the MyObject class or use any custom Equals/IEqualityComparer etc implementations.
public class MyObject
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Guid> Ids { get; set; }
}
...
// Get the list of existing objects (assume this returns some populated list)
List<MyObject> existingObjects = GetExistingObjects();
// Create a list of updated objects
List<MyObject> updatedObjects = new List<MyObject>()
{
new MyObject()
{
Ids = new List<Guid>() { new Guid("48af3cb9-945a-4ab9-91e4-7ee5765e5304"), new Guid("54b5128a-cf53-436c-9d88-2ef7abd15140") }
},
new MyObject()
{
Ids = new List<Guid>() { new Guid("0485382f-8f92-4a71-9eba-09831392ceb9"), new Guid("3d8b98df-caee-41ce-b802-2f0c5f9742de") }
}
};
// Do the comparison and capture the differences
List<MyObject> addedObjects = new List<MyObject>();
List<MyObject> removedObjects = new List<MyObject>();
List<MyObject> sameObjects = new List<MyObject>();
foreach (MyObject obj in updatedObjects)
{
if (existingObjects.Any(list => list.Ids.SequenceEqual(obj.Ids)))
{
sameObjects.Add(obj);
continue;
}
addedObjects.Add(obj);
}
foreach (MyObject obj in existingObjects)
{
if (!updatedObjects.Any(list => list.Ids.SequenceEqual(obj.Ids)))
{
removedObjects.Add(obj);
}
}
Here is a little shorter (due to elimination of the second loop) and little better (due to elimination of the ineffective search contained in the second loop). Still O(N^2) time complexity due to ineffective search contained in the loop though.
var addedObjects = new List<MyObject>();
var removedObjects = new List<MyObject>(existingObjects);
var sameObjects = new List<MyObject>();
foreach (var newObject in updatedObjects)
{
int index = removedObjects.FindIndex(oldObject => oldObject.Ids.SequenceEqual(newObject.Ids));
if (index < 0)
addedObjects.Add(newObject);
else
{
removedObjects.RemoveAt(index);
sameObjects.Add(newObject);
}
}
Update: A shorter, but IMO definitely not better (in fact worse performance wise) version
var addedObjects = updatedObjects.Where(newObject => !existingObjects.Any(oldObject => oldObject.Ids.SequenceEqual(newObject.Ids))).ToList();
var removedObjects = existingObjects.Where(oldObject => !updatedObjects.Any(newObject => newObject.Ids.SequenceEqual(oldObject.Ids))).ToList();
var sameObjects = updatedObjects.Where(newObject => !addedObjects.Any(addedObject => addedObject.Ids.SequenceEqual(newObject.Ids))).ToList();
If MyObject does not define custom equality comparison, i.e. uses default reference equality, the last line could be replaced with shorter and better performing
var sameObjects = updatedObjects.Except(addedObjects);
You can use Intersect and Except function in Linq
With Intersect you will get existing object,
and with Except you will get new objects.
Example of Except from MSDN:
double[] numbers1 = { 2.0, 2.1, 2.2, 2.3, 2.4, 2.5 };
double[] numbers2 = { 2.2 };
IEnumerable<double> onlyInFirstSet = numbers1.Except(numbers2);
foreach (double number in onlyInFirstSet)
Console.WriteLine(number);

What is the best way to trim a list?

I have a List of strings. Its being generated elsewhere but i will generate it below to help describe this simplified example
var list = new List<string>();
list.Add("Joe");
list.Add("");
list.Add("Bill");
list.Add("Bill");
list.Add("");
list.Add("Scott");
list.Add("Joe");
list.Add("");
list.Add("");
list = TrimList(list);
I would like a function that "trims" this list and by trim I want to remove all items at the end of the array that are blank strings (the final two in this case).
NOTE: I still want to keep the blank one that is the second item in the array (or any other one that is just not at the end) so I can't do a .Where(r=> String.isNullOrEmpty(r))
I would just write it without any LINQ, to be honest- after all, you're modifying a collection rather than just querying it:
void TrimList(List<string> list)
{
int lastNonEmpty = list.FindLastIndex(x => !string.IsNullOrEmpty(x));
int firstToRemove = lastNonEmpty + 1;
list.RemoveRange(firstToRemove, list.Count - firstToRemove);
}
If you actually want to create a new list, then the LINQ-based solutions are okay... although potentially somewhat inefficient (as Reverse has to buffer everything).
Take advantage of Reverse and SkipWhile.
list = list.Reverse().SkipWhile(s => String.IsNullOrEmpty(s)).Reverse().ToList();
List<T> (not the interface) has a FindLastIndex method. Therefore you can wrap that in a method:
static IList<string> TrimList(List<string> input) {
return input.Take(input.FindLastIndex(x => !string.IsNullOrEmpty(x)) + 1)
.ToList();
}
This produces a copy, whereas Jon's modifies the list.
The only solution I can think of is to code a loop that starts at the end of the list and searches for an element that is not an empty string. Don't know of any library functions that would help. Once you know the last good element, you know which ones to remove.
Be careful not to modify the collection while you are iterating over it. Tends to break the iterator.
I always like to come up with the most generic solution possible. Why restrict yourself with lists and strings? Let's make an algorithm for generic enumerable!
public static class EnumerableExtensions
{
public static IEnumerable<T> TrimEnd<T>(this IEnumerable<T> enumerable, Predicate<T> predicate)
{
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
var accumulator = new LinkedList<T>();
foreach (var item in enumerable)
{
if (predicate(item))
{
accumulator.AddLast(item);
}
else
{
foreach (var accumulated in accumulator)
{
yield return accumulated;
}
accumulator.Clear();
yield return item;
}
}
}
}
Use it like this:
var list = new[]
{
"Joe",
"",
"Bill",
"Bill",
"",
"Scott",
"Joe",
"",
""
};
foreach (var item in list.TrimEnd(string.IsNullOrEmpty))
{
Console.WriteLine(item);
}

In C#, What is the best way to see if a list contains another list?

If i have a list of strings, what is the best way to determine if every element in another list is contains in this list. For example:
List<string> list = new List<string>();
list.Add("Dog");
list.Add("Cat");
list.Add("Bird");
List<string> list2 = new List<string>();
list.Add("Dog");
list.Add("Cat");
if (list.ContainsList(list2))
{
Console.Write("All items in list2 are in list1")
}
I am trying to determine if there something like this "ContainsList" method?
if (!list2.Except(list).Any())
Loved SLaks version. Just for completeness, you can use HashSet method IsSubsetOf when performing set operations (also check IsSupersetOf method). There are pros and cons for this approach. Next code shows an example:
var list1 = new HashSet<string>{ "Dog", "Cat", "Bird" };
var list2 = new HashSet<string>{ "Dog", "Cat" };
if (list2.IsSubsetOf(list1))
{
Console.Write("All items in list2 are in list1");
}
Except method is streaming in nature. In query list2.Except(list1) list1 is buffered completely into memory, and you iterate one item at a time through list2. IsSubsetOf works eagerly in the opposite manner. This starts to make a difference when you have huge sets of data.
To analyse the worst case performance, here is some code from Except implementation at Monos Enumerable (dotPeek gives very similar results, just less readable)
var items = new HashSet<TSource> (second, comparer); //list1.Count
foreach (var element in first) //list2.Count
if (items.Add (element)) //constant time
yield return element;
as result O(list1.Count + list2.Count), loops aren't nested.
IsSubset has next method call, if second IEnumerable is HashSet (decompiled via dotPeek):
private bool IsSubsetOfHashSetWithSameEC(HashSet<T> other)
{
foreach (T obj in this) //list2.Count
if (!other.Contains(obj)) //constant time
return false;
return true;
}
Resulting in O(list2.Count) if list1 is a HashSet.
How about,
var list1 = new List<string>{"Dog","Cat","Bird"};
var list2 = new List<string>{"Dog","Cat"};
if (list1.Union(list2).SequenceEqual(list1))
Console.Write("All items in list2 are in list1");
How about this
list1.intersect (list2).ToList ().Foreach ((x)=>
{
Console.Writeline (x)
});

Create sequence consisting of multiple property values

I have an existing collection of objects with two properties of interest. Both properties are of the same type. I want to create a new sequence consisting of the property values. Here's one way (I'm using tuples instead of my custom type for simplicity):
var list = new List<Tuple<string, string>>
{ Tuple.Create("dog", "cat"), Tuple.Create("fish", "frog") };
var result =
list.SelectMany(x => new[] {x.Item1, x.Item2});
foreach (string item in result)
{
Console.WriteLine(item);
}
Results in:
dog
cat
fish
frog
This gives me the results I want, but is there a better way to accomplish this (in particular, without the need to create arrays or collections)?
Edit:
This also works, at the cost of iterating over the collection twice:
var result = list.Select(x => x.Item1).Concat(list.Select(x => x.Item2));
If you want to avoid creating another collection, you could yield the results instead.
void Main()
{
var list = new List<Tuple<string, string>>
{ Tuple.Create("dog", "cat"), Tuple.Create("fish", "frog") };
foreach (var element in GetSingleList(list))
{
Console.WriteLine (element);
}
}
// A reusable extension method would be a better approach.
IEnumerable<T> GetSingleList<T>(IEnumerable<Tuple<T,T>> list) {
foreach (var element in list)
{
yield return element.Item1;
yield return element.Item2;
}
}
I think your approach is fine and I would stick with that. The use of the array nicely gets the job done when using SelectMany, and the final result is an IEnumerable<string>.
There are some alternate approaches, but I think they're more verbose than your approach.
Aggregate approach:
var result = list.Aggregate(new List<string>(), (seed, t) =>
{
seed.Add(t.Item1);
seed.Add(t.Item2);
return seed;
});
result.ForEach(Console.WriteLine);
ForEach approach:
var result = new List<string>();
list.ForEach(t => { result.Add(t.Item1); result.Add(t.Item2); });
result.ForEach(Console.WriteLine);
In both cases a new List<string> is created.

Categories

Resources