Enumerable.ElementAt vs foreach
I开发者_JS百科 have a dictionary which I need to keep updated with incoming data, after parsing the incoming data I have to check if there are any entries in the dictionary which are not present in the incoming data (incoming data when parsed is a list and I need to map it with the dictionary entries).
To avoid multiple loops to removed the entries, I ran a decrementing for loop for dictionary count, then I fetch the dictionary key of the index using ElementAt, then check if the entry is present in the incoming data if not then I remove that entry from the list. I did this because running the foreach loop on the dictionary keys and removing from it will raise and exception as the dictionary keys collection would be modified.
I wanted to understand that doing this will there be any impact on execution time. I want to understand what is the order of ElementAt operation.
ElementAt
is useful if you need to provide indexing semantics and cannot guarantee that indexing semantics will be available in the underlying enumeration. It does use O(1) indexing when the enumeration acted upon is an IList<T>
(which includes List and arrays), but otherwise is O(n)*, which makes it being used in a sequence over everything go from the O(n) operation it would be with a list to O(n * n).
If however you got a copy of the keys with dict.Keys.ToList()
then you could safely foreach
through that, as it won't be changed by changes to your dictionary.
What isn't clear is why you don't just replace the old dictionary with the new one, which would be considerably faster again (simple reference assignment).
*Update: In the .NET Core version of linq there are a greater range of cases where ElementAt()
is O(1) such as the results of a Select()
done on an IList<T>
. Also OrderBy(…).ElementAt(…)
is now O(n) rather than O(n log n) as the combined sequence is turned into a quick-select rather than a quicksort followed by an iteration.
Use "mark then remove" trick as a workaround for inability to modify collection while iterating.
var dict = new Dictionary<int, string>
{
{3, "kuku" },
{1, "zOl"}
};
var newKeys = new List<int> { 1, 2, 4 };
var toRemove = dict.Keys.Except(newKeys).ToList();
foreach (var k in toRemove)
dict.Remove(k);
ElementAt()
does use the enumerator as pointed out, so if you want fastest index access you should use an array. Of course, that comes at the price of a fixed length, but if the size of the array is not constantly changing, it is feasible that Array.Resize()
might be the way to go.
Except()
seems like it would work here:
Dictionary<int, string> dict = new Dictionary<int, string>
{
{3, "kuku" },
{1, "zOl"}
};
IEnumerable<int> data = new List<int> { 1, 2, 4 };
IEnumerable<int> toRemove = dict.Keys.Except(data);
foreach(var x in toRemove)
dict.Remove(x);
I think that ElementAt()
uses the enumerator to get to the required element.
It will be the same as:
object returnedElement = null;
int i = 0;
foreach (var obj in dictionary.Keys)
{
if (i++ == at)
{
returnedElement = obj;
break;
}
}
You can get a Dictionary of the matching entries in target (Dictionary) and source (List) as follows:
using System;
using System.Collections.Generic;
using System.Linq;
Dictionary<string, string> target = new Dictionary<string, string>();
List<string> source = new List<string>();
target.Add("a", "this is a");
target.Add("b", "this is b");
source.Add("a");
source.Add("c");
target = Enumerable.Select(target, n => n.Key).
Where(n => source.Contains(n)).ToDictionary(n => n, k => target[k]);
It's not clear to me if you want to include new entries from the List into the Dictionary - if so, I am not sure what the new entry values would be if you only have a List of new incoming data.
精彩评论