This question already has answers here:
Whether a Dictionary can have Array as Key?
(3 answers)
An integer array as a key for Dictionary
(3 answers)
Closed 5 months ago.
I have a concurrent dictionary which maps an array of characters to an integer
var dictionary = new ConcurrentDictionary<char[], int>();
It works fine if I map an array
var key = new char[] { 'A', 'B' };
var value = 8;
dictionary.TryAdd(key, value);
Console.WriteLine(dictionary[key]);
gives
8
But if I use an array created from a queue it doesn't work
var qu = new Queue<char>();
qu.Enqueue('A');
qu.Enqueue('B');
Console.WriteLine(dictionary[qu.ToArray()]);
gives
Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'System.Char[]' was not present in the dictionary.
at System.Collections.Concurrent.ConcurrentDictionary`2.ThrowKeyNotFoundException(TKey key)
at System.Collections.Concurrent.ConcurrentDictionary`2.get_Item(TKey key)
at Program.<Main>$(String[] args) in C:\Users\User\Desktop\SubstitutionCipher\Program.cs:line 65
C:\Users\User\Desktop\SubstitutionCipher\bin\Debug\net6.0\SubstitutionCipher.exe (process 18688) exited with code -532462766.
What is wrong and how to fix it?
In order to find the item in the dictionary, the dictionary gets a hash code and does an equality check. For the char-arrays, this leads to a reference comparison. Even though the arrays do contain the same items, they are different objects, so the reference comparison fails. Hence, the item is not found.
There are several ways to solve this:
Either you help the dictionary to find the item by implementing a custom equality comparer - that is a class that implements IEqualityComparer<T> and is used as a constructor parameter when creating the dictionary.
Or you use a type that already implements a comparison that fits your needs. In the case of a char-Array, you could use a string as a simple alternative, e.g.
var key = new string(new char[] { 'A', 'B' });
var value = 8;
dictionary.TryAdd(key, value);
Console.WriteLine(dictionary[key]);
var qu = new Queue<char>();
qu.Enqueue('A');
qu.Enqueue('B');
key = new string(qu.ToArray());
Console.WriteLine(dictionary[key]);
See this fiddle to test.
Related
This question already has answers here:
When to use Cast() and Oftype() in Linq
(7 answers)
Closed 2 years ago.
Consider the following:
static void Main(string[] args)
{
List<object> someList = new List<object>();
someList.Add(1);
someList.Add(2);
someList.Add("apple");
someList.Add(true);
someList.Add(3);
}
The list items have not been defined with a data type and contains a boolean, a string, and an integer. Assuming there are many more list items of varying data types, how do I sum only the integers (such as 1, 2, and 3) from the list of objects using a simple loop or if statement? I'm just beginning in C#, thank you!
After searching, I've tried something like this, but with no success:
if(!someList.Contains(int))
List.Add(int);
If you know they are boxed int you could use OfType and Sum
var result = someList.OfType<int>().Sum();
Or for anything that is a representation of an int that would convert to a string representation, you could use Sum with ToString, and int.TryParse
var result = someList.Sum(x => int.TryParse(x.ToString(), out var value) ? value : 0);
Or if you like loops
var result = 0;
foreach (var value in someList.OfType<int>())
result += value;
// or
var result = 0;
foreach (var item in someList)
if (int.TryParse(item.ToString(), out var value))
result += value;
Additional Resources
Enumerable.OfType(IEnumerable) Method
Filters the elements of an IEnumerable based on a specified type.
Enumerable.Sum Method
Computes the sum of a sequence of numeric values.
Int32.TryParse Method
Converts the string representation of a number to its 32-bit signed
integer equivalent. A return value indicates whether the operation
succeeded.
This question already has answers here:
How to retrieve actual item from HashSet<T>?
(11 answers)
Make HashSet<string> case-insensitive
(7 answers)
Closed 6 years ago.
Given a hash set such as:
HashSet<string> names = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Alice",
"Bob",
"Charles",
}
How can I use this hash set to find the mapped value of a case insensitive string? For example, if I have a string "aLICe", I want to be able to find "Alice".
As soon as I posted the question I realized the answer is to simply use a case insensitive dictionary...
var names = new Dictionary<string, string>StringComparer.OrdinalIgnoreCase)
{
{"Alice","Alice"},
{"Bob","Bob"},
{"Charles","Charles"},
}
If I understand you correctly, you want to get the original value that was added to the hash set. So since “Alice” is the value in the hash set, you want to get that for inputs of “alice”, or “aLICe”.
Efficiently, this is not possible with a hash set. You would have to loop through the elements of the hash set to find the original value, making it as efficient as a list.
What you could do is have a dictionary instead which allows you to have a case-insensitive lookup but return the real value instead:
var names = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "Alice", "Alice" },
{ "Bob", "Bob" },
{ "Charles", "Charles" }
};
Console.WriteLine(names["alice"]); // Alice
Console.WriteLine(names["aLICe"]); // Alice
Well it's not terribly pretty but you could use:
string match = names.FirstOrDefault(n => names.Comparer.Equals(n,"aLice"));
Or use a Dictionary<string, string> instead, which is better at retrieving values. HashSet is good at storing unique values, but there's not a great mechanism to get the "matching" value out of it.
Am Facing a problem in Dictionaries.
Whether an Array can be a Key of a Value???
Dictionary<string[], int> di = new Dictionary<string[], int>();
di.Add(new string[]
{
"1","2"
}, 1);
di.Add(new string[]
{
"2","3"
}, 2);
MessageBox.Show(di[new string[] { "2", "3" }].ToString()); // Here KeyNotFoundException occurred.
Why Exception?
By default only references of the arrays would be compared, so you either have to
provide a custom IEqualityComparer<string[]> or
use a Tuple<string, string> as key instead ( since you only have two strings)
Here's a similar question's answer which shows how to create a custom comparer for the Dictionary- constructor.
No, actually you should not use arrays as a Dictionary<> Key; Dictionary<> when works with keys uses their hash codes which are computed as addresses:
String[] a = new[]{"1", "2"};
String[] b = new[]{"1", "2"};
a.GetHashCode() == b.GetHashCode(); // <- false
Arrays a and b have different addresses, and so different hash codes that's why
di.Add(a, 1);
di[b]; // <- error since a.GetHashCode() != b.GetHashCode()
Because a the Equals and GetHashCode functions of an array don't compare the content but the reference of the array himself.
This question already has answers here:
Error: "Cannot modify the return value" c#
(8 answers)
Closed 8 years ago.
I have a SortedDictionary defined as:
public SortedDictionary<DateTime,RosterLine> RosterLines = new SortedDictionary<DateTime,RosterLine>();
RosterLine itself is a simple struct:
struct RosterLine {
public string RosCd;
public string ActCd;
public double Hrs;
}
I can .Add(dt, rosterLine) no problems, and iterate through the dictionary fine too.
My problem is trying to update the RosterLine values given a specified date eg.
DateTime currDt = new DateTime(2013,12,02);
RosterLines[currDt].ActCd = "SO"; // error here
It tells me: Cannot modify the return value (dictionary def here) because it is not a variable. My goal is to do this with an iterating loop (which I thought might be the problem), but it won't work outside the loop on its own either (as above).
My question is: how do I update a SortedDictionary with a given key (date)?
The reason for the error message is that RosterLine is a struct and by that a value type. The error I get in ideone is:
Cannot modify a value type return value of
`System.Collections.Generic.SortedDictionary.this[System.DateTime]'.
Consider storing the value in a temporary variable
For value types, the dictionary stores a copy of the value and not a reference to the object on the heap. Also, when retrieving the value (as in dict[DateTime.Today]), it is copied again. Therefore, changing a property in the way you do in your sample only works on the copy of the value type. The compiler prevents misunderstandings by the error message - if it wouldn't one would wonder why the value in the dict has not been changed.
var dict = new SortedDictionary<DateTime, RosterLine>();
dict.Add(DateTime.Today, new RosterLine());
// Does not work as RosterLine is a value type
dict[DateTime.Today].ActCd = "SO";
// Works, but means a lot of copying
var temp = dict[DateTime.Today];
temp.ActCd = "SO";
dict[DateTime.Today] = temp;
In order to solve this, you could make RosterLine a class or you can work with temp variables as the error message suggests.
I have come across the following code in C# in Depth 2nd Edition by Jon Skeet and I don't understand how it works.
Dictionary<string,int> frequencies;
frequencies = new Dictionary<string,int>();
string[] words = Regex.Split(text, #"\W+");
foreach (string word in words)
{
if (frequencies.ContainsKey(word))
{
frequencies[word]++;
}
else
{
frequencies[word] = 1;
}
}
Specifically how does the "word" key get added to the dictionary? As I see it, a new dictionary is created called frequencies, it is empty. There is then a method to split a string called text into an array of string using Regex.Split. So far all good. Next there is a foreach loop which loops through the array, but the next part trips me up, it is checking if frequencies contains the particular word, if it does then increase the value of it by 1 or if it doesn't yet have a value set it to 1. But how does the dictionary get populated with the "word" key in the first place to allow it to be checked?
It looks to happen in this line
frequencies[word] = 1;
But I can't find a reference anywhere that says specifying a dictionary object followed by square brackets and an assignment to a value also populates the key. I thought you needed to use the add method of the dictionary instance or do so when initializing the dictionary.
If I am correct what is the name of this action?
frequencies[word] = 1;
is the same as calling
frequencies.Add(word, 1);
if the key word does not already exist. Otherwise you override the value.
When you call [something] on a dictionary you get a value by key something. The same goes for setting. When setting a value you can call dictionary[key] = value.
The function used is the [] operator (brackets operator).
I dove into the Object Browser and found this about the [] operator of the generic dictionary:
public TValue this[TKey key] { get; set; }
Member of System.Collections.Generic.Dictionary<TKey, TValue>
Summary: Gets or sets the value associated with the specified key.
Parameters: key: The key of the value to get or set.
Return Values: The value associated with the specified key. If the
specified key is not found, a get operation throws a
System.Collections.Generic.KeyNotFoundException, and a set operation
creates a new element with the specified key.
Exceptions: System.ArgumentNullException: key is null.
System.Collections.Generic.KeyNotFoundException: The property is
retrieved and key does not exist in the collection.