Prevent collection modified exception in a foreach loop in c# - c#

I'm iterating over a dictionary Dictionary<double, int> diametersAndQuantities and the dictionary is modified. Obviously I'm getting an exception that the collection has been modified. How can I prevent this from happening?
foreach (var diametersAndQuantity in diametersAndQuantities)
{
// some operations here
// update
diametersAndQuantities[db] = n;
}

You can create a temporary list of the KeyValuePairs to iterate over and still update the dictionary.
foreach (var diametersAndQuantity in diametersAndQuantities.ToList())
{
// some operations here
// update
diametersAndQuantities[diametersAndQuantity.Key] = n;
}

you can use the ToList() to get the enumeration to be evaluated
similar to accepted answer - maybe a little faster
should use less memory
Dictionary<int, int> dic = new Dictionary<int, int>() { { 1, 2 }, { 2, 3 }, { 3, 2 } };
foreach (int value in dic.Values)
Debug.WriteLine(value);
foreach (int key in dic.Keys.ToList())
dic[key] = 12;
foreach (int value in dic.Values)
Debug.WriteLine(value);
Debug.WriteLine("done");

Related

How to remove strings in list from another list?

I have 2 list which names are listA and listB.
I want to remove strings in listB which are in listA, but I want to do this in this way:
if listA contains: "bar", "bar", "bar", "foo"
and listB contains : "bar"
it removes only 1 bar and the result will be:
"bar", "bar", "foo"
the code I wrote removes all "bar":
List<string> result = listA.Except(listB).ToList();
You can try to remove it one by one:
foreach (var word in listB)
listA.Remove(word);
The Remove method will only remove one element at a time and is not throwing exception (but returning false) when the item is not found: https://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
var listA = new List<string>() { "bar", "bar", "bar", "foo" };
var listB = new List<string>() { "bar" };
foreach (var word in listB){
listA.Remove(word);
}
Here is a more efficient way to do that:
var countB = new Dictionary<string, int>(listB.Count);
foreach (var x in listB)
{
int count;
countB.TryGetValue(x, out count);
countB[x] = count + 1;
}
listA.RemoveAll(x =>
{
int count;
if (!countB.TryGetValue(x, out count)) return false;
if (count == 1)
countB.Remove(x);
else
countB[x] = count - 1;
return true;
});
This is a faster method but it is likely to change the order of elements of first list. Steps:
Map the listA to a Dictionary<string, int> (let's call it listAMap), where key is the element of the list and value is the total number of times that value has occurred in listA;
Iterate through listB and for every element of listB, if that element is in the listAMap, reduce its count;
Get the keys of listMapA using Keys property of C# dictionaries, and iterate through all the keys. For every key which has positive value, add that key to another list a total of its count times. So if an entry is "bar" -> 2, then add "bar" twice in the new list.
Total run time of the algorithm is O(m + n), where m and n are number of elements in both the original lists. It is a better running time than other approaches mentioned here which have O(m * n) running time. Obviously this algorithm uses more space.
Supportive Code for the algorithm above:
//Step-1: Create the dictionary...
var listAMap = new Dictionary<string, int>();
foreach (var listAElement in listA)
{
listAMap.ContainsKey(listAElement) ? listAMap[listAElement]++ : listAMap.Add(listAElement, 1);
}
// Step-2: Remove the listB elements from dictionary...
foreach (var listBElement in listB)
{
if (listAMap.Contains(listBElement)) listAMap[listBElement]--;
}
//Step-3: Create the new list from pruned dictionary...
var prunedListA = new List<string>();
foreach (var key in listAMap.Keys)
{
if (listAMap[key] <= 0) continue;
for (var count = 0; count < listAMap[key]; count++)
{
prunedListA.Add(key);
}
}
//prunedListA contains the desired elements now.

Using sortedList to count words in a List

For my homework, I have to use a SortedList to count words in a List with SortedList taking each entry and sorting it in alphabetical order before inserting. When it comes to display the data to the user, the data displayed should be displayed with sorting according to value instead of key.
Below is my attempt at this but I am getting 3 errors and I don't know how to resolve it. I am not allowed to use LINQ for this.
List<string> words = new List<string>(); <--- Already populated
This is my code of this implementation and I get 3 errors:
SortedList<string, int> d = new SortedList<string, int>();
bool InsideOfList = false;
foreach (string word in words)
{
InsideOfList = false;
foreach (KeyValuePair<string, int> keyvalPair in d)
{
if (keyvalPair.Key == word)
{
keyvalPair.Value += 1;
InsideOfList = true;
}
}
if (InsideOfList == false)
{
d.Add(word,1);
}
}
//Now instead of sorting by key I want to sort by value instead
SortedList<int, string> tempSortList = new SortedList<int, string>();
foreach (KeyValuePair<string, int> keyvalPair in d)
{
//trying to swap the value of previous SortedList with the Key of the new SortedList
tempSortList.Add(keyvalPair.Value, keyvalPair.Key);
}
for (int i = 0; i < 20; i++)
{
Console.WriteLine("\t{0}:\t{1}", tempSortList.GetKey(i), tempSortList.GetByIndex(i));
}
Here are my errors:
Property or indexer 'System.Collections.Generic.KeyValuePair<string,int>.Value' cannot be assigned to -- it is read only
'System.Collections.Generic.SortedList<int,string>' does not contain a definition for 'GetKey'
'System.Collections.Generic.SortedList<int,string>' does not contain a definition for 'GetByIndex'
You are confusing two things here. One is SortedList() and other is SortedList().
GetKey and GetKeyList are not present in SortedList(). You can use this instead of GetKey
tempSortList.ElementAt(index); // This will return you a KeyValuePair.
And for the first error you cannot assign value keyvalPair.Value has only getter. So you cannot set its value by doing += 1.
This is not quite good. Needs some improvement but it will work.
for (int i = 0; i < d.Count; i++)
{
if (d.ElementAt(i).Key == word)
{
d.Values[i] += 1;
}
}
or
for (int i = 0; i < d.Count; i++)
{
if (d.ElementAt(i).Key == word)
{
var val = d.ElementAt(i).Value + 1;
d.RemoveAt(i);
d.Add(word, val);
}
}
Please modify this line and check if it works. it should.
Console.WriteLine("\t{0}:\t{1}", tempSortList.GetKey(i), tempSortList.GetByIndex(i));
to
var key = tempSortedList.Keys[i];
var value = tempSortedList.Values[i];
Console.WriteLine("\t{0}:\t{1}", key, value);

using a for loop to iterate through a dictionary

I generally use a foreach loop to iterate through Dictionary.
Dictionary<string, string> dictSummary = new Dictionary<string, string>();
In this case I want to trim the entries of white space and the foreach loop does however not allow for this.
foreach (var kvp in dictSummary)
{
kvp.Value = kvp.Value.Trim();
}
How can I do this with a for loop?
for (int i = dictSummary.Count - 1; i >= 0; i--)
{
}
what about this?
for (int i = dictSummary.Count - 1; i >= 0; i--) {
var item = dictSummary.ElementAt(i);
var itemKey = item.Key;
var itemValue = item.Value;
}
KeyValuePair<TKey, TValue> doesn't allow you to set the Value, it is immutable.
You will have to do it like this:
foreach(var kvp in dictSummary.ToArray())
dictSummary[kvp.Key] = kvp.Value.Trim();
The important part here is the ToArray. That will copy the Dictionary into an array, so changing the dictionary inside the foreach will not throw an InvalidOperationException.
An alternative approach would use LINQ's ToDictionary method:
dictSummary = dictSummary.ToDictionary(x => x.Key, x => x.Value.Trim());
You don't need to use .ToArray() or .ElementAt(). It is as simple as accessing the dictionary with the key:
dictSummary.Keys.ToList().ForEach(k => dictSummary[k] = dictSummary[k].Trim());

Convert Dictionary<string, string> to CSV File. Keys as Row 1 and Values as row value for the columns

I have a C# Dictionary which I create by reading multiple data sources. The Dictionary contains key value pairs where the value collection of a key is a comma seperated string value.
for example:
Dictionary<string, string> d = new Dictionary<string, string>();
d.Add("cat", "2,2");
d.Add("dog", "10, A");
d.Add("llama", "A,B");
d.Add("iguana", "-2,-3");
I want the final csv file to look like this:
cat, dog, llama, iguana
2,10,A,-2
2,A,B,-3
How do I achieve this?
It would be easier if your data structure was Dictionary, otherwise you'll need to split out the items in advance or do it multiple times in a loop. A List would work also. Depending on how you're getting the data in from your data sources should determine whether it is easier to do a String.Split() on the data coming in (e.g. it's already a delimited string), or whether each item is being added individually.
This code could be optimized (e.g. get rid of the dictionary lookups each time through the loops) and cleaned up, but should get you started, and should be fine if your data set is not too large:
static void Main(string[] args)
{
Dictionary<string, string[]> d = new Dictionary<string, string[]>();
d.Add("cat", new string[] { "2", "2" });
d.Add("dog", new string[] { "10", "A" });
d.Add("llama", new string[] { "A", "B" });
d.Add("iguana", new string[] { "-2", "-3" });
// Not clear if you care about the order - this will insure that the names are in alphabetical order.
// The order of items in a dictionary are not guarenteed to be the same as the order they were added.
var names = d.Keys.OrderBy(l => l).ToList();
// Not clear if you know the number of items that will be in the list in advance - if not, find the max size
int maxSize = d.Values.Max(a => a != null ? a.Length : 0);
Console.WriteLine(String.Join(", ", names));
for (int i = 0; i < maxSize; i++)
{
foreach (string name in names)
{
string[] value = d[name];
if ((value != null) && (i < value.Length))
{
Console.Write(value[i]);
}
if (name != names.Last())
{
Console.Write(",");
}
}
Console.WriteLine();
}
}
Will generate this output:
cat, dog, iguana, llama
2,10,-2,A
2,A,-3,B
A Dictionary used in a foreach will return a KeyValuePair ... where you can access the "Key" and "Value". For the extracting of the "value" you can use string.Split(). The rest should be relativly easy depending on what you exactly need.
[Edit]
And finally you just open a text file for write, and dump the data out the way you want.
Dictionary<string, List<string>> d = new Dictionary<string, List<string>>();
d.Add("cat", new List<string> {"2", "2"});
d.Add("dog", new List<string> {"10", "A"});
d.Add("llama", new List<string>{"A","B"});
d.Add("iguana", new List<string>{"-2","-3"});
List<List<string>> values = d.Values.AsQueryable().ToList();
int count = values[0].Count;
for (int i = 0; i < count; i++)
{
for (int j = 0; j < values.Count; j++)
{
Console.Write(values[j].ElementAt(i));
}
}
Ommited checks and formatting but this does what you want.

How to change all values in a Dictionary<string, bool>?

So I have a Dictionary<string, bool> and all I want to do is iterate over it and set all values to false in the dictionary. What is the easiest way to do that?
I tried this:
foreach (string key in parameterDictionary.Keys)
parameterDictionary[key] = false;
However I get the error: "Collection was modified; enumeration operation may not execute."
Is there a better way to do this?
Just change the enumeration source to something other than the dictionary.
foreach (string key in parameterDictionary.Keys.ToList())
parameterDictionary[key] = false;
For .net 2.0
foreach (string key in new List<TKey>(parameterDictionary.Keys))
parameterDictionary[key] = false;
In .net 5 the following snippet no longer throws:
var d = new Dictionary<string, int> { { "a", 0 }, { "b", 0 }, { "c", 0 }};
foreach (var k in d.Keys){
d[k] = 1;
}

Categories