3

I am working with a list of dictionaries containing string arrays. The dictionaries are defined/filled via a loop over a DataTable. In the following code test evaluates to false (twice), can somebody tell me why?

List<Dictionary<string[], int>> mydix = new List<Dictionary<string[], int>>();

mydix.Add(new Dictionary<string[], int>()); 
mydix.Add(new Dictionary<string[], int>()); 
mydix.Add(new Dictionary<string[], int>()); 

string[] s = {"tree"};
mydix[1].Add(s, 1);
bool test = mydix[1].ContainsKey(s); // This evaluates to true, which I understand
var entry= mydix[1][s]; // This is 1

DataTable dt=new DataTable();
dt.Columns.Add("test");
dt.Rows.Add(new string[] {"key"});            
mydix[2].Add(dt.Rows[0].ItemArray.Select(x => x.ToString()).ToArray(), 2);
test = mydix[2].ContainsKey(new string[] { "key" }); // Why does this evaluate to false?

// Here is an example with an array with two elements
DataTable dt2 = new DataTable(); 
dt2.Columns.Add("test");
dt2.Columns.Add("test2");    
string[] t={"tree1","tree2"}; 
dt2.Rows.Add(t);
mydix[0].Add(dt2.Rows[0].ItemArray.Select(x => x.ToString()).ToArray(), 3);
test = mydix[0].ContainsKey(t); // Why does this evaluate to false? 
nawfal
  • 70,104
  • 56
  • 326
  • 368
user2546346
  • 145
  • 1
  • 1
  • 8
  • [This](http://stackoverflow.com/questions/670063/getting-hash-of-a-list-of-strings) helped me to generalize the hash function to strings – user2546346 Jul 28 '13 at 07:54

2 Answers2

1

Hopefully someone will correct me if I'm wrong, but it's my understanding that when you call ContainsKey, the Dictionary has a private method (exploring in dotPeek), which runs to decide wether the objects you're comparing are equal or not.

Depending on what type you're using for the key, a different equality comparison will occur, based on various implementations of IEqualityComparer, this way the most appropriate comparison can be run, based on the types you wish to compare.

You're using string arrays as the keys, so you're essentially checking the equality of the array objects themselves, not their contents. So it's entirely correct that your ContainsKey is returning false, you aren't asking your Dictionary if it contains the same array as a key, you're asking it if it contains a different array, which happens to contain the same contents.

The IEqualityComparer GetHashCode method in this case (an array), will return a hash based on the reference of the object, not the contents.

If you wanted this behaviour, the magic Mr Skeet has written a custom IEqualityComparer<T> for arrays in this post:

Compare Objects?

Community
  • 1
  • 1
Chris
  • 8,268
  • 3
  • 33
  • 46
1

The problem is that the string array you are using as the key to the dictionary does object comparison, not content comparison.

In order to support this type of data as a key, the easiest solution is to use an IEqualityComparer.

First, create the comparer (this is a sample; yours will need additional sanity checking and logic):

    private class ArrayComparer : IEqualityComparer<string[]>
    {
        public bool Equals(string[] item1, string[] item2)
        {
            if (item1[0] == item2[0])
            {
                return true;
            }
            else
            {
                return false;
            }
        }


        public int GetHashCode(string[] item)
        {
            return item[0].GetHashCode();
        }

Then, change the instantiation of your dictionaries to use this new comparer:

    mydix.Add(new Dictionary<string[], int>(new ArrayComparer()));
    mydix.Add(new Dictionary<string[], int>(new ArrayComparer())); 
    mydix.Add(new Dictionary<string[], int>(new ArrayComparer())); 

Once you have done this, both tests will return true.

competent_tech
  • 44,465
  • 11
  • 90
  • 113
  • Thank you! This indeed solves my problem. I am still new to C# and as you can see I am somewhat struggling with the objects referencing. I am now struggling with generalizing this to string arrays with more than 1 element: DataTable dt2 = new DataTable(); dt2.Columns.Add("test"); dt2.Columns.Add("test2"); string[] t={"tree1","tree2"}; dt2.Rows.Add(t); mydix[0].Add(dt2.Rows[0].ItemArray.Select(x => x.ToString()).ToArray(), 3); test = mydix[0].ContainsKey(t); // Why does this evaluate to false? – user2546346 Jul 28 '13 at 07:12
  • My code in this comment is not really readable. I have included the generalization in my original post (see the last example). Can you help me with that? I can easily extend public bool Equals to check every element of the arrays but how do I extend GetHasCode to item[2]? – user2546346 Jul 28 '13 at 07:19