the given key was not present in the dictionary error debugging - c#

When I debug the following code, it always throws the following exception:
The given key was not present in the dictionary.
I need help figuring it out.
string current;
Dictionary<string, List<int>> map = new Dictionary<string, List<int>>();
for (int i = 0; i < y; i++){
current = lines[i].material.Text + "," + lines[i].profilid.Text;
if (map[current] == null){
map[current] = new List<int>();
}
map[current].Add(i);
material_profile.Add(current);
}
foreach (KeyValuePair<string, List<int>> entry in map){
List<int> lenghts = new List<int>();
// do something with entry.Value or entry.Key
for (int i = 0; i < entry.Value.Count(); i++){
int stueckzahl = int.Parse(lines[entry.Value[i]].stueck.Text);
int laenge = int.Parse(lines[entry.Value[i]].länge.Text);
for (int j = 0; j < stueckzahl; j++){
lenghts.Add(laenge);
}
}
}

The code map[current] == null attempts to fetch the value from map[current], and this will throw an error if there is no item.
If you wish to attempt to fetch an item which may not be present, you need to use the TryGetValue method.
This will work:
List<int> list;
if (!(map.TryGetValue(current, out list)))
{
list= new List<int>();
map.Add(current, list);
}
list.Add(i);

As Andrew Shepherd pointed out, you can't look up a value that does not exist in a Dictionary.
For your test to make sense, you would have to expect map[current] to actually return the value null, but that would require the dictionary to actually contain a key-value pair where the key is the same value as current, and the value is null. In your case, that key does not exist at all.
The simplest solution to your problem may be to replace this line...
if (map[current] == null)
...with the following:
if (!map.ContainsKey(current))
That will search for the key without throwing an exception if it is not found.
PS: You could also use map.Add(current, new List<int>); on the proceeding line instead of the []-syntax - I personally think using .Add(..) makes the code a little simpler, but that may be a matter of preference.

Related

Possible to modify a List while iterating through it?

I have the following:
foreach (var depthCard in depthCards)
{
var card = InternalGetCard(db, depthCard.CardId);
var set = InternalGetSet(db, (int)card.ParentSetId);
var depthArray = InternalGetDepthArrayForCard(db, set.SetId);
foreach (var cardToUpdate in set.Cards)
{
// do stuff
SaveChanges(db);
// since I already took care of it here, remove from depthCards
depthCards.Remove(depthCardToUpdate);
}
}
This isn't working though because I'm modifying the collection in the middle of a loop. Is there some type of collection that does allow this type of access?
I don't want to ToList() the depthCards because I already have them and I want to modify that list as I'm iterating. Is this possible?
It's possible, the trick is to iterate backwards:
for (int i = depthCards.Count - 1; i >= 0; i--) {
if (depthCards[i] == something) { // condition to remove element, if applicable
depthCards.RemoveAt(i);
}
}
You can iterate backwards with a for-loop
for (int i = depthCards.Count - 1; i >= 0; i--)
{
depthCards.RemoveAt(i);
}
or if you just want to remove items on a condition, use List.RemoveAll:
depthCardToUpdate.RemoveAll(dc => conditionHere);
You can create a custom enumerator that handles this for you. I did this once and it was a bit tricky but worked after some finesse.
See: http://www.codeproject.com/Articles/28963/Custom-Enumerators

Modifying Values in a For Each Loop - Any Way?

I know this goes against the .NET rules, but sometimes I need it. For example, I need to run through a Dictionary<string, bool>. This dictionary stores my variables in a parsed logic equation. I want to output a truth table, so I need to iterate through and set elements.
One thing I've tried is
foreach (var x in Variables.Keys)
{
bool on = ((in) & (j << in)) > 0;
Variables[x] = on;
builder.Append(on == true ? '1' : '0').Append('\t');
j++;
}
I just get a InvalidOperationException: Collection was modified; enumeration operation may not execute. I converted the dictionary to an array and tried to modify it that way, but KeyValuePair.Value is readonly, so it won't work.
You could create a copy of the key collection:
foreach (var x in Variables.Keys.ToArray())
You cannot edit Dictionary<TKey, TValue> in loop.
Try this:
var resultVariables = new Dictionary<string, bool>(Variables.Count);
foreach (var x in Variables.Keys)
{
bool on = ((in) & (j << in)) > 0;
resultVariables[x] = on;
builder.Append(on == true ? '1' : '0').Append('\t');
j++;
}
Variables = resultVariables;
I create a copy of the collection first, then I'd iterate over the copy allowing me to mess with the original as required.

List divide and conquer - Pass by value or reference

I m not on .NET 4.
I get a huge list from a data source. When the number of elements in the list are higher than X i like to partition the list, assign each partition to a thread. after processing partitions i like to merge them.
var subsets = list.PartitionEager(50000);
//var subsets = list.Partition<T>(50000);
Thread[] threads = new Thread[subsets.Count()];
int i = 0;
foreach (var set in subsets)
{
threads[i] = new Thread(() => Convertor<T>(set));
threads[i].Start();
i++;
}
for (int j = 0; j < i; j++)
{
threads[j].Join();
}
Convertor method is a static method that takes a list and does some lookup.
public static void Convertor<T>(List<T> list) where T : IInterface {
foreach (var element in list)
{
**// do some lookup and assing a value to element
// then do more lookup and assign a value to element**
}
}
When i run this code, even though i know that most of the elements will be assigned a value. They are in fact coming back null.
I m aware that the copy of the list will be passed to the method but any change to the element should be reflected in the upper method. however this is happening only within the final subset.
I even added some code to merge the lists into a single one.
list.Clear();
foreach (var set in subsets)
{
list.AddRange(set);
}
code for paritioning:
public static List<List<T>> PartitionEager<T>(this List<T> source, Int32 size)
{
List<List<T>> merged = new List<List<T>>();
for (int i = 0; i < Math.Ceiling(source.Count / (Double)size); i++)
{
merged.Add(new List<T>(source.Skip(size * i).Take(size)));
}
return merged;
}
What am i doing wrong? how to resolve this issue? i d like the elements to be assigned values after the lookups? is this related to synchronization or parameter passing?
If .NET 4 is an option, you can just use Parallel.For or Parallel.ForEach. These methods automatically handle partitioning for you, as well as providing many other advantages in terms of scalability across multiple degrees of concurrency on different systems.
Looks like you're having modified closure while creating threads. If I'm correct then all your threads update the same (last) set. Modify the code in this way:
foreach (var set in subsets)
{
var setLocalCopy = set;
threads[i] = new Thread(() => Convertor<T>(setLocalCopy));
threads[i].Start();
i++;
}

Assigning values from list to list excluding nulls

Sorry for the title but I don't know other way of asking.
EDITED FOR EASY EXPLANATION
public Dictionary<string, string>[] getValuesDB(DataSet ds)
{
Dictionary<string, string>[] info;
Dictionary<string, string>[] temp = new Dictionary<string, string>[ds.Tables[0].Rows.Count];
Dictionary<string, string> d;
string ciclo = "CICLO 02";
try
{
for (int c = 0; c < ds.Tables[0].Rows.Count; c++)
{
d = new Dictionary<string, string>();
OracleConn ora = OracleConn.getInstancia();
DataSet oraDs = ora.getClientByCycle(ds.Tables[0].Columns["IDCliente"].Table.Rows[c].ItemArray[1].ToString(), ciclo);
if (oraDs.Tables[0].Rows.Count > 0)
{
oraDs.Tables[0].Columns[5].Table.Rows[0].ToString();
d.Add("DomID", ds.Tables[0].Columns["DomID"].Table.Rows[c].ItemArray[0].ToString());
d.Add("ClientID", ds.Tables[0].Columns["ClientID"].Table.Rows[c].ItemArray[1].ToString());
d.Add("AccountType", ds.Tables[0].Columns["AccountType"].Table.Rows[c].ItemArray[2].ToString());
temp[c] = d;
}
}
}
catch (Exception eo)
{
}
int count = 0;
for (int i = 0; i < temp.Length; i++)
{
if (temp[i] != null)
count++;
}
info = new Dictionary<string, string>[count];
return info;
NOW I need to get all the non-null values from 'temp' and put it in info
Any idea
Try this
Dictionary<string, string> dictionaryWithoutNulls ;
dictionaryWithoutNulls = dictionaryWithNulls
.Where(d => d.Value != null)
.ToDictionary(s=>s.Key, s=>s.Value);
Simple Answer
A simple, direct, not-pretty answer that works with .NET 2.0 and disturbs as little code as possible:
// ...code
info = new Dictionary<string, string>[count];
// Beginning of new code
int j = 0;
foreach (Dictionary<string, string> fromTemp in temp)
{
if (fromTemp == null)
continue;
info[j] = fromTemp;
j++;
}
// End of new code
return info;
Better Answer
However, there are several problems with your code snippet that should be corrected. Please forgive me if you already know these things and just did not do them for the sake of brevity. If that's the case, hopefully someone else will be able to learn from these comments.
1. The return type: Dictionary<string, string>[]
An array of dictionaries isn't very self explanatory and doesn't seem very useful. Dictionaries are perfect for looking up a value by a key, but once you place a bunch of these dictionaries in an array and remove the nulls, it seems like it would be pretty difficult to figure out which dictionary you need.
Since I don't know what you plan to do with the result of this method, it's hard to recommend an alternative. Maybe try an IDictionary<string, IDictionary<string, string>>, using ds.Tables[0].Columns["IDCliente"].Table.Rows[c].ItemArray[1].ToString() for the key of the outer dictionary. That would make it easier to locate values in the result. Or you could try an IList<IDictionary<string, string>>. Either of those would be able to auto-resize as you add new elements, eliminating the need to count the non-nulls and allocate a new array. Auto-resizing hurts performance, so initialize the collection with an appropriate capacity if you can.
Edit
After reviewing your code again, it appears that each dictionary gets the same three keys. It would be more appropriate to use a class for this:
public class ClientInfo
{
private string _domID;
public string DomID
{
get { return _domID; }
set { _domID = value; }
}
// ... also add properties for ClientID and AccountType
}
Then the return value could be something like IList<ClientInfo>.
2. Querying the database inside a loop
I'm guessing that ora.getClientByCycle(...) is a database query. It's almost always possible to rewrite your query or batch queries together so that you only have to make one round-trip to the database. Even if it's a fast query it's still probably going to be the slowest line in this method. Query the database once rather than ds.Tables[0].Rows.Count times.
3. Swallowing System.Exception
// !!! Don't do this!!!
catch (Exception eo)
{ }
If the database query might fail in certain cases and you're OK with that, then catch (OracleException). Always catch the most specific exception you can. Otherwise the catch block could catch and ignore other exceptions that you weren't expecting. Then your program doesn't behave correctly and you have no helpful exception information to help you debug it. For more information, see http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx.

Loop through System.Collections.Generic.Dictionary via for statement

I have a quick question. Is there way to easy loop through System.Collections.Generic.Dictionary via for statement in C#?
Thanks in advance.
You can use foreach:
Dictionary<string,string> dictionary = new Dictionary<string,string>();
// ...
foreach (KeyValuePair<string,string> kv in dictionary)
{
string key = kv.Key;
string value = kv.Value;
}
Not in a reasonable way, no. You could use the Linq extension ElementAt:
for (int i = 0; i < dictionary.Keys.Count; i++)
{
Console.WriteLine(dictionary.ElementAt(i).Value);
}
...but I really don't see the point. Just use the regular foreach approach. If you for some reason need to keep track of an index while iterating, you can do that "on the side":
int index = 0;
foreach (var item in dictionary)
{
Console.WriteLine(string.Format("[{0}] - {1}", index, item.Value));
// increment the index
index++;
}
There are several ways.
Looping through the keys:
foreach(var key in myDictionary.Keys)
looping through values:
foreach(var value in myDic.Values)
looping through pairs:
foreach(KeyValuePair<K, V> p in myDic)
{
var key = p.Key;
var value = p.Value
}
No, for is mostly for collections with indices.
You can foreach over it easily, however.
If you are using Net 3.5 or later then you can use LINQ and a predecate to locate a specific Value or Values.
Dictionaries do not necessarily store their KeyValue pairs in order (either by entry order nor Key order).
Philippe's got it for foreach, though I usually simplify it to:
foreach (var pair in dictionary)
{
var key = pair.Key;
var value = pair.Value;
}
There's no way to loop through this collection of key-value pairs using a for loop because they aren't stored in order. You can loop through the keys or the values as collections though.
It can ofc be done, but it is a bit silly IMO
Dictionary<string,string> dictionary = new Dictionary<string, string>();
Dictionary<string, string>.Enumerator enumerator = dictionary.GetEnumerator();
for (int i = 0; i < attributeValues.Count;i++ )
{
KeyValuePair<string, string> current = enumerator.Current;
//do something usefull
enumerator.MoveNext();
}
the only thing gained by this is a (fairly useless) index, and if that is the actual goal, you are better served by something like this:
int currentIndex = 0;
foreach (KeyValuePair<string, string> keyValuePair in dictionary)
{
//do something usefull
currentIndex++;
}
If it does not have to be a "for"-loop, maybe using the Dictionary.GetEnumerator Method with a "while"-loop is an option - it is pretty easy as well IMHO:
var enumerator = d.GetEnumerator();
while (enumerator.MoveNext())
{
var pair = enumerator.Current;
b += pair.Value;
}
enumerator.Dispose();
code-snippet from C# Dictionary GetEnumerator
You can loop over the keys
for(int i = 0; i < myDictionary.Keys.Length; ++i)
myDictionary.Keys[i] ...
or the values
for(int i = 0; i < myDictionary.Values.Length; ++i)
myDictionary.Values[i] ...
Or both as Philippe shows

Categories

Resources