updating a KeyValuePair where the key is not an int - c#

I am trying to make a change to a KeyValuePair and have got as far as this code from another post I found:
var keysToModify = this.Properties
.Where(k => k.Key == propertyName)
.ToList();
foreach (var key in keysToModify)
{
this.Properties[key] = newValue;
}
But my key field is a string not an int. What would I need to do to update a KeyValuePair in this case?
EDIT : My KeyValuePair is of type string,string, and when I run the code in my example above, I get the error :
keyvaluepair is not assignable to parameter type int

There is no particular magic to updating the value of the pair when the key is a string - it's much the same, you're just using a string to access a value (and this stands for any type being used as key, omitting any possible peculiarities).
But the only thing I can think might be throwing you off in this case is the loop. The loop is iterating instances, though, and isn't exposing an additive "index" in doing so, but rather key in this case would be a string.

The key wasn't an integer in your linked question either, it was a Customer.
You should be able to use the same general method no matter what the key type is.
Here's example code like the accepted answer to the other question that will work when the key is a string:
// The ToList() call here is important, so that we evaluate all of the query
// *before* we start modifying the dictionary
var keysToModify = CustomerOrderDictionary.Keys
.Where(k => k == "MyString")
.ToList();
foreach (var key in keysToModify)
{
CustomerOrderDictionary[key] = 4;
}
Note that where the other answer accessed the Id property of the Customer key, you can directly compare the key to your target string.
Also, note that if you are just updating a single entry in the dictionary you don't need a loop, you can just do it directly. If you know the dictionary dictionary has an entry with the given key, or don't mind creating a new entry, just use:
CustomerOrderDictionary["MyString"] = 4;
or, to guard against creating new entry:
if (CustomerOrderDicionary.ContainsKey("MyString"))
{
CustomerOrderDictionary["MyString"] = 4;
}

Related

Get single value from dictionary by key

var listaFirme = new Dictionary<string, string>
{
{ "foo", "bar" }
};
var matchKey = "foo";
return listaFirme.Where(pair => pair.Key == matchKey).Select(pair => pair.Value).ToString();
I know that the keys are unique, so I want to return one value from my Dictionary. In this case it doesn't work, as it returns the string "System.IEnumerable<String>"...
It really does seem like you're overcomplicating this issue.
You can just use the indexer ([]) of the Dictionary class along with the .ContainsKey() method.
If you use something like this:
string value;
if (myDict.ContainsKey(key))
{
value = myDict[key];
}
else
{
Console.WriteLine("Key Not Present");
return;
}
You should achieve the effect that you want.
If you want to retrieve a value of a key from a dictionary access by indexer or TryGetValue:
var value = listaFirme[matchKey];
//If you don't know for sure that dictionary contains key
string value;
if(a.TryGetValue(matchKey, out value))
{
/* Code here */
}
As for why you got the result you did: Linq operations of Where and Select return an IEnumerable<T> so when doing ToString on it it executes the ToString of IEnumerable which is to print it's type.
Notice that listaFirme isn't a good name for a dictionary
If you did not have a dictionary and wanted to return one item then you'd use FirstOrDefault:
var value = someList.FirstOrDefault(item => /* some predicate */)?.Value;
It seems that you've overcomplicated the usage. In this case you don't need Linq.
Just use the Dictionary provided indexer: listaFirme[matchKey]. This returns the related value. IF the key does not exist the Dictionary throws a KeyNotFoundException exception. If you want to check if the key exists you can use the ContainsKey() method which returns a bool.
Replace toString(); with FirstOrDefault();
When you are applying .Where() condition it will return an Enumerable. You have to either cast it to list using .ToList() then you will get list of the values that meet the condition you used, or if you just want to get the first one you can use FirstOrDefault();
You can also write it like this
listaFirme.FirstOrDefault(pair => pair.Key == matchKey).Value
Since FirstOrDefault() accepts a predicate, you don't need always need to use .Where.

Searching a String in a Dictionary

I have this Class Form1.cs where I created my GUI, It also has a combobox with following functionality:
string SelectedItemName = (string)comboBox2.SelectedItem.ToString();
Console.WriteLine(SelectedItemName);
if (comboBox2.SelectedIndex > -1)
{
testvariabel2.GetSessionName();
}
So I check if the user has chose something from the ComboBox and than I call the Function GetSessionName in my other Class CTestRack.cs.
Dictionary<string, Dictionary<string, string>> newDictionary = new Dictionary<string,Dictionary<string, string>>();
foreach (SectionData section in data.Sections)
{
var keyDictionary = new Dictionary<string, string>();
foreach (KeyData key in section.Keys)
keyDictionary.Add(key.KeyName.ToString(), key.Value.ToString());
newDictionary.Add(section.SectionName.ToString(), keyDictionary);
if (newDictionary.ContainsKey(testvariabel.SelectedItemName))
{
Console.WriteLine("Key: {0}, Value: {1}", keyDictionary[testvariabel.SelectedItemName]);
}
else Console.WriteLine("Couldn't check Selected Name");
}
Here I'd like to check if the String SelectedItemName exists in my Dictionary, but I always get the Systen.ArgumentNullException that the String SelectedItemName is NULL in my CTestRackClass.
Now my question, how do I search the dictionary in CTestRack for the String setted in the other Class
Form1 ?
Well... actually you got the looking into the dictionary right ! To find out if a key is present in a dictionary, you use ContainsKey.
if(myDictionary.ContainsKey(myKey))
{
//do something
}
However, your problems comes from the fact that null is never a valid key in a dictionary (mainly because null doesn't have a proper hash code). So you need to ensure that the key you're looking for is not null. From your code, I guess that testvariabel.SelectedItemName has not been set as it should.
Moreover there is a more efficient way to look if a value is present before doing something with it. Use TryGetValue:
TValue val;
if(myDictionary.TryGetValue(myKey, out val))
{
//do something with val
}
That way you don't need to access myDictionary[myKey]. If you use ContainsKey, you are in fact accessing the same value twice. That a small cost in most cases, but very easy to avoid, so you should give it a try.
Note that I answered only the specific question about looking into a dictionary. I cannot say anything about the correctness of your code as a whole.
I see there are two clear issues with your code.
You are checking if a key exists in one dictionary (newDictionary) but trying to retrieve it from another (keyDictionary )
You are try to look up keys in the dictionary even before it's completely built. Move the if check outside of foreach loop.

Remove KeyValue from ObservableCollection most effective way?

I have following code to remove group from collection. Technically, there should be no duplicates, but it does remove all anyway. Any trick with LINQ to .Remove.Where.. ?
public void DeleteGroup(KeyValuePair<int, string> group)
{
while (this.Groups.Any(g => g.Key.Equals(group.Key)))
{
var groupToRemove = this.Groups.First(g => g.Key.Equals(group.Key));
this.Groups.Remove(groupToRemove);
}
}
Assuming you are passing in a KeyValuePair with the same Key and the same Value this is the most efficient way possible with an ObseravableCollection.
public void DeleteGroup2(KeyValuePair<int, string> group)
{
Groups.Remove(group);
}
This works because a KeyValuePair is a structure and when the overloaded operator == is applied it is comparing both the Key and the Value data members of the structure.
Again this will work just fine if you pass in the exact same Key and Value that is contained in the Groups obserabableCollection...if the Value does not match it will not work.
Behind the scenes an ObserableCollection is pretty much a list so it will have to iterate over every item performing the == operator. The same is true for the code you are posting. Just because it is using LINQ doesn't mean it's any more efficient. It's not like the LINQ where clause is using any indexing like it would be with LINQ to SQL.
public void DeleteGroup3(KeyValuePair<int, string> groupToDelete)
{
var itemsToDelete =
(
from g in Groups
where g.Key == groupToDelete.Key
select g
);
foreach (var kv in itemsToDelete)
{
Groups.Remove(kv);
}
}
This would probably be most efficient method using linq if you want to guarantee that you remove all items even those with duplicate keys.
public void DeleteGroup4(KeyValuePair<int, string> group)
{
List<int> keyIndexes = new List<int>();
int maxIndex = Groups.Count;
for (int i = 0; i < maxIndex; i++)
{
if (Groups[i].Key == group.Key)
{
keyIndexes.Add(i);
}
}
int indexOffset = 0;
foreach (int index in keyIndexes)
{
Groups.RemoveAt(index - indexOffset);
indexOffset++;
}
}
This should have the best performance of all of them if you have multiple items with the same key or you don't know the exact same Key Value pair as the original.
I believe your DeleteGroup method is BIG O of 2N^2...N for the outer Any while Loop and N for the First and N for the Remove. Take outer Loop times the sum of the inside and you get 2N^2
DeleteGroup2 is BIG O of N and had the best performance of all of them. The drawback is that you need to know both the Key and the Value not just the Key. It will also only remove the first item it finds. It won't delete duplicate items with the same Key and the same Value.
DeleteGroup3 IS BIG O of N + N^2. N for the select. Worse case is that your key is in there N times so N^2 for the removal.
DeleteGroup4 is BIG O of 2N. N to find the indexes and in worst case if you have all items with the same key then its N to remove each of them as RemoveAtIndex is a Big O of 1. This has the best performance if you only know the Key and you have the possibility of having multiple items with the same Key.
If you know for a fact that you won't have duplicate items I would use DeleteGroup2. If you have the possibility of having duplicates DeleteGroup4 should have the best performance.
On a side note if won't have duplicates and you don't necessarily know both the Key and the Value you can still use the best performing option of DeleteGroup2 but create a class called KeyValueIntString with properties of Key and Value. Then overide the IsEquals method so that it only compares the Key property unlike the KeyValue struct that compares both the Key and the Value data members. Then you can use the ObserableCollection.Remove method and not have to worry about knowing the value that is stored. I.E. you could pass in instance of a KeyValueIntString that has the Key set but you don't have to worry about setting the Value property.
After commenting I decided to Add best readability method although it does have worse performance. Has a Big O of N^4. N for select, N for ToList, N for ForEach and N for Remove.
public void DeleteGroup5(KeyValuePair<int, string> groupToDelete)
{
(
from g in Groups
where g.Key == groupToDelete.Key
select g
).ToList().ForEach(g => Groups.Remove(g));
}

Loop in Dictionary

I use this:
foreach(KeyValuePair<String,String> entry in MyDic)
{
// do something with entry.Value or entry.Key
}
The problem is that I can't change the value of entry.Value or entry.Key
My question is that how can i change the value or key when looping through a dictionary?
And, does dictionary allow duplicated key? And if yes, how can we avoid ?
Thank you
You cannot change the value of a dictionary entry while looping through the items in the dictionary, although you can modify a property on the value if it's an instance of a reference type.
For example,
public class MyClass
{
public int SomeNumber { get; set;}
}
foreach(KeyValuePair<string, MyClass> entry in myDict)
{
entry.Value.SomeNumber = 3; // is okay
myDict[entry.Key] = new MyClass(); // is not okay
}
Trying to modify a dictionary (or any collection) while looping through its elements will result in an InvalidOperationException saying the collection was modified.
To answer your specific questions,
My question is that how can i change the value or key when looping through a dictionary?
The approach to both will be pretty much the same. You can either loop over a copy of the dictionary as Anthony Pengram said in his answer, or you can loop once through all the items to figure out which ones you need to modify and then loop again through a list of those items:
List<string> keysToChange = new List<string>();
foreach(KeyValuePair<string, string> entry in myDict)
{
if(...) // some check to see if it's an item you want to act on
{
keysToChange.Add(entry.Key);
}
}
foreach(string key in keysToChange)
{
myDict[key] = "new value";
// or "rename" a key
myDict["new key"] = myDict[key];
myDict.Remove(key);
}
And, does dictionary allow duplicated key? And if yes, how can we avoid ?
A dictionary does not allow duplicate keys. If you want a collection of <string, string> pairs that does, check out NameValueCollection.
Updating the dictionary in the loop is going to be a problem, as you cannot modify the dictionary as it is being enumerated. However, you can work around this pretty easily by converting the dictionary to a list of KeyValuePair<> objects. You enumerate that list, and then you can modify the dictionary.
foreach (var pair in dictionary.ToList())
{
// to update the value
dictionary[pair.Key] = "Some New Value";
// or to change the key => remove it and add something new
dictionary.Remove(pair.Key);
dictionary.Add("Some New Key", pair.Value);
}
For the second part, the key in a dictionary must be unique.
KeyValuePair's Key and value are read only. But you can change a value like that:
dictionary[key].Value = newValue;
But if you want to change the key, you will have to remove/add a key.
And no, a Dictionary does not allow duplicate keys, it will throw an ArgumentException.
You cannot modify keys while enumerating them.
One method I use for changes to the collection while enumerating them is that I do break; out of the foreach loop when a match is found and item is modified, and am restarting the whole enumeration all over again. That's one way of handling it...
No, Dictionary can't have duplicate keys. If you want something that will sort by key and allow duplicates, you should use some other data structure.
You can do this like
for (int i = 0; i < MyDic.Count; i++)
{
KeyValuePair<string, string> s = MyDic.ElementAt(i);
MyDic.Remove(s.Key);
MyDic.Add(s.Key, "NewValue");
}
And Dictionary doesn't allow duplicates

Get index of a key/value pair in a C# dictionary based on the value

I would like to know if some property or method exists that gets the index of a specific value.
I found that dictionaries have the Contains() method which returns true if the value passed in exists, so this method almost implements what I need.
I know that I can loop through all the value pairs and check the condition, but I ask because maybe there's an optimized way of doing this.
Let's say you have a Dictionary called fooDictionary
fooDictionary.Values.ToList().IndexOf(someValue);
Values.ToList()
converts your dictionary values into a List of someValue objects.
IndexOf(someValue)
searches your new List looking for the someValue object in question
and returns the Index which would match the index of the Key/Value pair in the dictionary.
This method does not care about the dictionary keys, it simply returns the index of the value that you are looking for.
This does not however account for the issue that there may be several matching "someValue" objects.
There's no such concept of an "index" within a dictionary - it's fundamentally unordered. Of course when you iterate over it you'll get the items in some order, but that order isn't guaranteed and can change over time (particularly if you add or remove entries).
Obviously you can get the key from a KeyValuePair just by using the Key property, so that will let you use the indexer of the dictionary:
var pair = ...;
var value = dictionary[pair.Key];
Assert.AreEqual(value, pair.Value);
You haven't really said what you're trying to do. If you're trying to find some key which corresponds to a particular value, you could use:
var key = dictionary.Where(pair => pair.Value == desiredValue)
.Select(pair => pair.Key)
.FirstOrDefault();
key will be null if the entry doesn't exist.
This is assuming that the key type is a reference type... if it's a value type you'll need to do things slightly differently.
Of course, if you really want to look up values by key, you should consider using another dictionary which maps the other way round in addition to your existing dictionary.
Consider using System.Collections.Specialized.OrderedDictionary, though it is not generic, or implement your own (example).
OrderedDictionary does not support IndexOf, but it's easy to implement:
public static class OrderedDictionaryExtensions
{
public static int IndexOf(this OrderedDictionary dictionary, object value)
{
for(int i = 0; i < dictionary.Count; ++i)
{
if(dictionary[i] == value) return i;
}
return -1;
}
}
You can find index by key/values in dictionary
Dictionary<string, string> myDictionary = new Dictionary<string, string>();
myDictionary.Add("a", "x");
myDictionary.Add("b", "y");
int i = Array.IndexOf(myDictionary.Keys.ToArray(), "a");
int j = Array.IndexOf(myDictionary.Values.ToArray(), "y");
You can use LINQ to help you with this.
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "hi");
dict.Add(2, "NotHi");
dict.Add(3, "Bah");
var item = (from d in dict
where d.Value == "hi"
select d.Key).FirstOrDefault();
Console.WriteLine(item); //Prints 1
If searching for a value, you will have to loop through all the data. But to minimize code involved, you can use LINQ.
Example:
Given Dictionary defined as following:
Dictionary<Int32, String> dict;
You can use following code :
// Search for all keys with given value
Int32[] keys = dict.Where(kvp => kvp.Value.Equals("SomeValue")).Select(kvp => kvp.Key).ToArray();
// Search for first key with given value
Int32 key = dict.First(kvp => kvp.Value.Equals("SomeValue")).Key;
In your comment to max's answer, you say that what you really wanted to get is the key in, and not the index of, the KeyValuePair that contains a certain value. You could edit your question to make it more clear.
It is worth pointing out (EricM has touched upon this in his answer) that a value might appear more than once in the dictionary, in which case one would have to think which key he would like to get: e.g. the first that comes up, the last, all of them?
If you are sure that each key has a unique value, you could have another dictionary, with the values from the first acting as keys and the previous keys acting as values. Otherwise, this second dictionary idea (suggested by Jon Skeet) will not work, as you would again have to think which of all the possible keys to use as value in the new dictionary.
If you were asking about the index, though, EricM's answer would be OK. Then you could get the KeyValuePair in question by using:
yourDictionary.ElementAt(theIndexYouFound);
provided that you do not add/remove things in yourDictionary.
PS: I know it's been almost 7 years now, but what the heck. I thought it best to formulate my answer as addressing the OP, but of course by now one can say it is an answer for just about anyone else but the OP. Fully aware of that, thank you.
no , there is nothing similar IndexOf for Dictionary although you can make use of ContainsKey method to get whether a key belongs to dictionary or not

Categories