I am not a particularly confident programmer yet but am getting there.
My problem is that I have a
static Dictionary<string, Dictionary<string, List<string>>> testDictionary = ...
If the Dictionary doesn't contain the current key (string), I can easily add the key and another dictionary that has been populated, like so...
testDictionary.Add(userAgentResult, allowDisallowDictionary);
That works fine, my problem comes when I am trying to add the inner dictionary if the userAgentResult Key already exists.
I was hoping to do it this way...
testDictionary[userAgentResult].Add(allowDisallowDictionary);
but the .Add method wants two arguments, i.e. the string key and list value. So I went on to write this code...
//this list as the dictionary requires a list
List<string> testDictionaryList = new List<string>();
//this method returns a string
testDictionaryList.Add(regexForm(allowResult, url));
//this will add the key and value to the inner dictionary, the value, and then
//add this value at the userAgentKey
testDictionary[userAgentResult].Add(allowDisallowKey, testDictionaryList);
This also works, my problem is that this dictionary is added to numerous times, and when the inner dictionary already contains the key that is trying to be added, it obviously errors. So when
I would probably simplify this by having one dictionary and joining the keys thus "simulating" a grouping.
string key = userAgentResult + allowDisallowKey;
static Dictionary<string, List<string> testDictionary = ...
testDictionary[key] = list;
You simply need to manage one dictionary.
In this case what you need to do is not adding an entry to the inner dictionary. You need to add the value to the key-value pair of the outer dictionary. Only this time the value happens to be yet another dictionary :)
testDictionary[userAgentResult] = allowDisallowDictionary;
Maybe i don't get your problem. First make sure that dictionaries exist like so:
if (!testDictionary.ContainsKey(userAgentResult))
testDictionary[userAgentResult] = new Dictionary<string, List<string>>();
if (!testDictionary[userAgentResult].ContainsKey(allowDisallowKey))
testDictionary[userAgentResult][allowDisallowKey] = new List<string>();
Then you are free to add items like so:
testDictionary[userAgentResult][allowDisallowKey].Add("some value");
testDictionary[userAgentResult][allowDisallowKey].AddRange(someValueList);
When using nested dictionaries i normally use this approach:
private static Dictionary<string, Dictionary<string, List<string>>> _NestedDictionary = new Dictionary<string, Dictionary<string, List<string>>>();
private void DoSomething()
{
var outerKey = "My outer key";
var innerKey = "My inner key";
Dictionary<string, List<string>> innerDictionary = null;
List<string> listOfInnerDictionary = null;
// Check if we already have a dictionary for this key.
if (!_NestedDictionary.TryGetValue(outerKey, out innerDictionary))
{
// So we need to create one
innerDictionary = new Dictionary<string, List<string>>();
_NestedDictionary.Add(outerKey, innerDictionary);
}
// Check if the inner dict has the desired key
if (!innerDictionary.TryGetValue(innerKey, out listOfInnerDictionary))
{
// So we need to create it
listOfInnerDictionary = new List<string>();
innerDictionary.Add(innerKey, listOfInnerDictionary);
}
// Do whatever you like to do with the list
Console.WriteLine(innerKey + ":");
foreach (var item in listOfInnerDictionary)
{
Console.WriteLine(" " + item);
}
}
You need to do the same for the inner dictionary that you did for the outer one. First check if a list already exists for this key. If not create it. Then use the list that either existed or was created.
Related
I've been searching through here and I realize that similar questions have been asked before, and I've gone over several, but what seems to be correct doesn't seem to be working for.
I have the following method which pulls a set of key value pairs in a DataSet gathered from a SQL query.
public static Dictionary<string, string> LoadConfiguration()
{
DataSet sqlResults = data.GetSettings();
Dictionary<string, string> dict =
new Dictionary<String, String>();
foreach (DataRow dr in sqlResults.Tables[0].Rows)
{
dict.Add(
dr.Field<string>("key"), dr.Field<string>("value")
);
}
return dict;
}
It's as simple as it gets as far as I can see, but I've never actually worked with a dictionary before as I'm relatively new to any kind of real programming.
I know that my Dictionary has the expected data, because if I iterate through it with a foreach, it gives me the expected results
foreach (KeyValuePair<string, string> i in MyStuff.Configuration.LoadConfiguration())
{
Console.WriteLine("Key = " + i.Key + " Value = " + i.Value);
}
which gives me the following which is all I've implemented so far
Key = EnableLdap Value = false
Key = LdapPath Value = LDAP://DC=some,DC=domain,DC=com
Key = SessionTimeout Value = 60
But I only want to select a specific value from a key named EnableLdap for example
My understanding is that both the following methods would be correct. But when I try this way, I get a KeyNotFoundException
Dictionary<string,string> dict = MyStuff.Configuration.LoadConfiguration();
Console.WriteLine(dict["EnableLdap"]);
If I try this I get a [InvalidOperationException: Sequence contains no matching element]
Dictionary<string,string> dict = MyStuff.Configuration.LoadConfiguration();
Console.WriteLine(dict.Single(s => s.Key == "EnableLdap").Value);
And if I try this I just get nothing
string value = "";
if (dict.TryGetValue("EnableLdap", out value))
{
dict["EnableLdap"] = value;
Console.WriteLine(value);
}
I'm pretty certain I'm missing something simple here and the answer will result in a facepalm
SOLUTION:
I got caught by trailing spaces as was suggested a few times in the comments. The solution was to simply add some trimming to my row values when adding them to my Dictionary.
public static Dictionary<string, string> LoadConfiguration()
{
DataSet sqlResults = data.GetSettings();
Dictionary<string, string> dict =
new Dictionary<String, String>();
foreach (DataRow dr in sqlResults.Tables[0].Rows)
{
dict.Add(
dr.Field<string>("key").Trim(), dr.Field<string>("value").Trim()
);
}
return dict;
}
It is one of these 2 issues.
The key does not exist in the dictionary, this could be due to a spelling mistake or even a leading/trailing space or something of that nature.
The key exists but is cased differently than you think it is.
For option 2 you can ensure that casing is not an issue by providing a StringComparer to the dictionary as an argument in the constructor.
Dictionary<string, string> dict = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
The above code snippet will ensure that the dictionary creates the hash for each key based on the ordinal case insensitive representation of the string key. This will allow for the following example to pass:
dict["enableldap"] = "true";
Console.WriteLine(dict["EnableLdap"]);
See StringComparer for more options.
My understanding is that both the following methods would be correct. But when I try this way, I get a KeyNotFoundException
It seems that your dictionary doesn't have that key. Keep in mind that dictionaries are key-sensitive you can check if a key exist by using this method:
Console.WriteLine("Exist key "+ dict.ContainsKey("EnableLdap"));
If I try this I get a [InvalidOperationException: Sequence contains no matching element]
Dictionary<string,string> dict = MyStuff.Configuration.LoadConfiguration();
Console.WriteLine(dict.Single(s => s.Key == "EnableLdap").Value);
The linQ method .Single expects to find a value. If no value is found that Exception will be thrown. You should be using .SingleOrDefault if you don't want that exception, but beware of nulls.
And if I try this I just get nothing.
TryGetValue doesn't throw any exceptions. It just tries.
I'd suggest you to add a format for your keys either uppercased or lowercased, when you're creating the dictionary.
foreach (DataRow dr in sqlResults.Tables[0].Rows)
{
dict.Add(
dr.Field<string>("key").ToUpperCase(), dr.Field<string>("value")
);
}
and then you can query it like this:
if(dict.ContainsKey("ENABLELDAP"))
{
string value = dict["ENABLELDAP"];
}
I have an output of a method that provides two key pieces of data per result.
Eg: 20160503 and nzdusd.
How can I store this in a dictionary and the sort against either one?
I want to be able to sort against key or value but can't get past adding the data!
Is there another data structure better suited?
Previously I tried simply calling Add()
But got an ArgumentException with message, An item with the same key has already been added..
Dictionary<string, string> missingDays = new Dictionary<string, string>();
// Some logic
missingDays.Add(formattedDate, fxPair); // Exception here
foreach (var missingDay in missingDays)
{
Console.WriteLine(missingDay);
}
A dictionary must have unique key values. In this case the formattedDate variable is being assigned the same value more than once, which causes an exception the second time you try to add an entry to the dictionary with the same formattedDate value.
You can use a Tuple<string, string> inside a list (List<Tuple<string, string>>) to store the values, and then use the LINQ method OrderBy() to sort the list.
var missingDays = new List<Tuple<string, string>>();
missingDays.Add(Tuple.Create("exampleFormattedDate", "exampleFxPair"));
foreach (var missingDay in missingDays.OrderBy(md => md.Item1))
{
Console.WriteLine(missingDay);
}
I am declaring a Dictionary inside a Dictionary like:
var something = new Dictionary<string, Dictionary<string, Object>>();
I want to be able to access both the outer dictionary as well as the inner dictionary with an IgnoreCase StringComparer.
var something = new Dictionary<string, Dictionary<string, Object>>(StringComparer.InvariantCultureIgnoreCase);
As I am only calling the constructor of the outer dictionary, how can I set the StringComparer of the inner dictionary? If I can't call it's constructor, I can see that there is a property Comparer but I'm not sure how I can get access to the inner dictionary object instead of just a Key or Value.
Any suggestions?
When you declare:
var something = new Dictionary<string, Dictionary<string, Object>>();
It has not created any inner dictionary yet. You will initialize inner dictionary when you add data to it, e.g.
if(!something.ContainsKey("somekey"))
{
something["somekey"] = new Dictionary<string, Object>(StringComparer.InvariantCultureIgnoreCase);
}
I have some lines from text files that i want to add into the Dictionary.I am using Dictionary for the first time.While adding up starting lines it was Ok but suddenly i got error:
An item with the same key has already been added
Here in my code there are duplicate keys which i can not change.Here is my code in c#
Dictionary<string, string> previousLines = new Dictionary<string, string> { };
previousLines.Add(dialedno, line);
Here dialedno is the key and line is the textfile line.
Here is the code from which i am retrieving the given line based on key.
string tansferOrginExt = previousLines[dialedno];
So my concern is how to allow to add duplicate keys in Dictionary if possible and if not how can i get similar functionality.
how to allow to add duplicate keys in Dictionary
It is not possible. All keys should be unique. As Dictionary<TKey, TValue> implemented:
Every key in aDictionary<TKey, TValue> must be unique according to
the dictionary's equality comparer.
Possible solutions - you can keep collection of strings as value (i.e. use Dictionary<string, List<string>>), or (better) you can use Lookup<TKey, TValue> instead of dictionary.
how to check for duplicate keys and delete previous value from
Dictionary?
You can check if the key exists with previousLines.ContainsKey(dialedno) but if you always want to hold the last line, then just replace whatever dictionary had for the key, or add the new key if it is not in the dictionary:
previousLines[dialedno] = line;
We can Use a List of Key Value Pair
List<KeyValuePair<string, string>> myduplicateLovingDictionary= new List<KeyValuePair<string, string>>();
KeyValuePair<string,string> myItem = new KeyValuePair<string,string>(dialedno, line);
myduplicateLovingDictionary.Add(myItem);
Its not possible to add duplicate items to a Dictionary - an alternative is to use the Lookup class.
Enumerable.ToLookup Method
Creates a generic Lookup from an IEnumerable.
Example:
class Program
{
private static List<KeyValuePair<string, int>> d = new List<KeyValuePair<string, int>>();
static void Main(string[] args)
{
d.Add(new KeyValuePair<string, int>("joe", 100));
d.Add(new KeyValuePair<string, int>("joe", 200));
d.Add(new KeyValuePair<string, int>("jim", 100));
var result = d.Where(x => x.Key == "joe");
foreach(var q in result)
Console.WriteLine(q.Value );
Console.ReadLine();
}
}
List< KeyValuePair < string, string>> listKeyValPair= new List< KeyValuePair< string, string>>();
KeyValuePair< string, string> keyValue= new KeyValuePair< string, string>("KEY1", "VALUE1");
listKeyValPair.Add(keyValue);
If your question is if you can add the same key twice, the answer is No.
However if you want to just iterate through the item and then increase the count of the value for the particular Key, you can achieve that by using "TryAdd" method.
var dict = new Dictionary<int, int>();
foreach (var item in array)
{
dict.TryAdd(item, 0);
dict[item]++;
}
The same thing we are trying to achieve with if else, can be achieved with this method.``
https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.tryadd?view=netframework-4.7.2
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