Removing values from a dictionary using loop - c#

I have procedure where I want to remove values from a dictionary based on a certain condition(comparing the key values to another dictionary)
In the foreach loop I am however not allowed to modify the dictionary.
Is there perhaps a better way of doing this?
The key values are string,
foreach (var archive in dictArchivedTitles)
{
foreach (var kvp in dictAllTheFiles)
{
if (kvp.Key == archive.Key)
{
dictAllTheFiles.Remove(kvp.Key);
}
}
}

You can do the following.
foreach (var archive in dictArchivedTitles)
{
if(dictAllTheFiles.ContainsKey(archive.Key)
dictAllTheFiles.Remove(archive.Key);
}

Change
foreach (var kvp in dictAllTheFiles)
to
foreach (var kvp in dictAllTheFiles.ToList())
That way you would work on a copy of dictAllTheFiles

If removing the key of already archived titles is all you want in these nested loops, this would be more efficient and readable (imho):
dictAllTheFiles = dictAllTheFiles.Keys.Except(dictArchivedTitles.Keys)
.ToDictionary(key => key);
Note that you need to addd using System.Linq;

If you do not want to modify either dictionary and have access to LINQ, you could do this:
var nonMatchingEntries =
dictAllTheFiles.Where(kv => !dictArchivedTitles.ContainsKey(kv.Key));

Related

Is there any way to edit the Values of a Dictionary during iteration?

I want to change all the values that fulfill a certain criterium in a C# dictionary.
Simply editing the values like this
foreach (var kv in dictionary)
{
kv.Value += 1;
}
does not work because the KeyValuePair of the foreach loop is read only.
However, editing the entries directly like this:
foreach (var kv in dictionary)
{
dictionary[kv.Key] = kv.Value + 1;
}
also doesn't work, because it modifies the collection and breaks the iterator.
At this point, the only remaining solution I can think of is storing all keys of the dictionary in a list, and then using that to edit the values during a second loop, however, that seems like a pretty inelegant solution to me.
Is there any better alternative?
You could create an array (or a List with .ToList()) from the .Keys in the foreach, something like this:
foreach (string key in dictionary.Keys.ToArray())
{
dictionary[key] += 1;
}
You can use ToDictionary to create new Dictionary and change the origin in the loop, like :
foreach (var kv in dictionary.ToDictionary(k=>k.Key,v=>v.Value))
{
dictionary[kv.Key] = kv.Value + 1;
}
I hope you find this helpful.

How to dynamically remove key value pair from a dictionary if the key contains some substring?

I have an outer loop iterating over an array of substrings to be matched in the dictionary. In the inner loop I want to iterate over the dictionary and delete an entry whose key contains a substring.How to do this without getting "Collection was modified Exception"?
foreach (string outerKey in new string[] { "PAYERADDR_PAYERNAME", "RECADDR_RECNAME", "PAYERADDR_ADDR", "RECADDR_ADDR" })
{
foreach (var item in _generalWorksheetData.Where(kvp => kvp.Value.Contains(outerKey)).ToList())
{
_generalWorksheetData.Remove(item.Key);
}
}
You need a new collection:
List<string> todelete = dictionary.Keys.Where(k => k.Contains("substring")).ToList();
todelete.ForEach(k => dictionary.Remove(k));
or with a foreach:
foreach (string key in todelete)
dictionary.Remove(key); // safe to delete since it's a different collection
If Dictionary.Keys implemented IList instead of just ICollection you could access it in a backwards for-loop to remove them. But since there is no indexer you can't.
AFAIK, you can't. However you can store those pairs in a list and delete them in a separate loop from the first.
Just update your inner foreach as following:
foreach (var item in _generalWorksheetData.Keys.Where(kvp => kvp.Contains(outerKey)).ToList())
{
_generalWorksheetData.Remove(item);
}
Note that LINQ extension methods ToList and ToArray do allow you to modify collections.
List<string> sampleList = new List<string>();
sampleList.Add("1");
sampleList.Add("2");
sampleList.Add("3");
sampleList.Add("4");
sampleList.Add("5");
// Will not work
foreach (string item in sampleList)
{
sampleList.Remove(item);
}
// Will work
foreach (string item in sampleList.ToList())
{
sampleList.Remove(item);
}
Find match and remove the entries as below
var keysWithMatchingValues = dictionary.Where(d => d.Key.Contains("xyz"))
.Select(kvp => kvp.Key).ToList();
foreach(var key in keysWithMatchingValues)
dictionary.Remove(key);

Modifying the contents of a Dictionary in foreach loop

I am trying to update the contents of a C# Dictionary in a foreach loop, when a certain condition is met.
foreach (KeyValuePair<int, Corpus_22_04_2014_StreetTable_Row> entry in id_StreetNameDictionary)
{
if(something_happens())
{
Corpus_22_04_2014_StreetTable_Row r = entry.Value;
//Modify r
id_StreetNameDictionary[entry.Key] = r;
}
}
This throws an InvalidOperationException stating that "Collection was modified; enumeration operation may not execute.". It seems that we are not allowed to modify the contents of a Dictionary in foreach loop.
What can be a possible workaround to that?
You can simply use ToList on the dictionary to copy each key-value pair into a list. Then iterate over that list instead of Dictionary:
foreach (var entry in id_StreetNameDictionary.ToList())
{
if(something_happens())
{
Corpus_22_04_2014_StreetTable_Row r = entry.Value;
//Modify r
id_StreetNameDictionary[entry.Key] = r;
}
}
Create a list of all the keys and iterate that. For example:
foreach (var key in id_StreetNameDictionary.Keys.ToList())
{
if(something_happens())
{
var r = id_StreetNameDictionary[key];
//Modify r
id_StreetNameDictionary[key] = r;
}
}
This will be somewhat more memory efficient than creating a list of KeyValuePair structures from the dictionary.
Since it appears you really want to modify the row and not necessarily the Dictionary, then do so directly:
foreach (var entry in id_StreetNameDictionary)
{
if (something_happens())
{
((Corpus_22_04_2014_StreetTable_Row)entry.Value)["FieldToChange"] = newValue;
//...repeat for each field to change...you will be altering the row directly, no need to reassign it
}
}
Well I use the following workaround. Not very efficient maybe, but it does the work.
I declare three dictionaries:
Dictionary<int, Corpus_22_04_2014_StreetTable_Row> id_StreetNameDictionary = new Dictionary<int, Corpus_22_04_2014_StreetTable_Row>();
Dictionary<int, Corpus_22_04_2014_StreetTable_Row> tempDictionary = new Dictionary<int, Corpus_22_04_2014_StreetTable_Row>();
Dictionary<int, Corpus_22_04_2014_StreetTable_Row> swapBuffer;
I completely dump the old dictionary, with all modified and unmodified key/value pairs, into the tempDictionary. Then I swap both dictionaries and clear the unused one:
foreach (var entry in id_StreetNameDictionary)
{
Corpus_22_04_2014_StreetTable_Row row = id_StreetNameDictionary[entry.Key];
if (something_happens())
{
//Modify row
}
tempDictionary.Add(row.id, row);
}
swapBuffer = tempDictionary;
tempDictionary = id_StreetNameDictionary;
id_StreetNameDictionary = swapBuffer;
tempDictionary.Clear();

Getting rid of duplicates inside List<> contained within a dictionary

I have the following dictionary.
Dictionary<string, List<string>> dictSubjects = new Dictionary<string, List<string>>();
and I am trying to get rid of potential duplicates residing within each list instace of the respective dictionary entry.
This is what I have tried but get and error along the lines of the list being read only
foreach (var kvp in dictSubjects)
{
lstSubjectsNoDupes.Clear();
for (int i = kvp.Value.Count - 1; i >= 0; i--)
{
if(lstSubjectsNoDupes.Contains(kvp.Value[i]))
{
lstSubjectsNoDupes.Add(kvp.Value[i]);
}
}
kvp.Value = lstSubjectsNoDupes;
}
How can I effectively get rid of potential duplicates within each list of my Dictionary?
The simplest way if you don't care too much about efficiency would be:
dictSubjects = dictSubjects.ToDictionary(pair => pair.Key,
pair => pair.Value.Distinct().ToList());
Alternatively, to update the existing dictionary:
foreach (var key in dictSubjects.Keys.ToList())
{
dictSubjects[key] = dictSubjects[key].Distinct().ToList();
}
Note the use of ToList here to avoid iterating over a view of a collection which is being modified. Without this, InvalidOperationException is thrown.
What about
foreach (var kvp in dictSubjects.ToList())
dictSubjects[kvp.Key] = kvp.Value.Distinct().ToList();

how to change Dictionary's value when enumerate it?

how to change Dictionary's value when enumerate it?
the following code doesn't work, because we can not change dictionary's value when enumerating it. Is there any way to get around it? Or NO WAY? Thanks
foreach (KeyValuePair<string, int> kvp in mydictionary)
{
if (otherdictionary.ContainsKey(kvp.Key))
{
mydictionary[kvp.Key] = otherdictionary[kvp.Key];
}
else
{
otherdictionary[kvp.Key] = mydictionary[kvp.Key];
}
}
The simplest way would be to take a copy first. As you only want the key value pairs, you might as well put them in a list rather than building a new dictionary though. Also, you can avoid doing quite as many lookups using TryGetValue.
var copy = myDictionary.ToList();
foreach (KeyValuePair<string, int> kvp in copy)
{
int otherValue;
if (otherdictionary.TryGetValue(kvp.Key, out otherValue))
{
mydictionary[kvp.Key] = otherValue;
}
else
{
otherdictionary[kvp.Key] = kvp.Value;
}
}
Make a copy of the values you need to enumerate over before you enumerate over them, then you can change the original source.
Since you don't actually use the value, you can change the code to this:
foreach (string key in mydictionary.Keys.ToArray())
if (otherdictionary.ContainsKey(key))
mydictionary[key] = otherdictionary[key];
else
otherdictionary[key] = mydictionary[key];
Note the use of .ToArray() there to make a temporary array copy of the key collection. This is now separate from the source dictionary, so you can change the dictionary all you want.
another option, copy the keys collection to an array and use it in for each loop -
string[] arr1 = new string[mydictionary.Count];
mydictionary.Keys.CopyTo(arr1,0);
foreach (string j in arr1)
{
if (otherdictionary.ContainsKey(j))
{
mydictionary[j] = otherdictionary[j];
}
else
{
otherdictionary[j] = mydictionary[j];
}
}

Categories

Resources