Dictionary Says XYZ Key Doesn't Exist When It Does

Dictionary Says XYZ Key Doesn't Exist When It Does

Anonymous
Not applicable
636 Views
3 Replies
Message 1 of 4

Dictionary Says XYZ Key Doesn't Exist When It Does

Anonymous
Not applicable

Hello

 

I have a dictionary where the keys are XYZ objects and the values are boolean. I am attempting to check if a key exists in the dictionary.

 

My Problem: If the dictionary contains the key new XYZ(1,1,1) and I go to check if the dictionary contains this key using myDictionary.ContainsKey(new XYZ(1,1,1) it always returns false.

 

Why is this happening and how can I fix this? I am aware I am comparing doubles which requires a degree of tolerance but shouldn't the XYZ.Equals() class compensate for this like many other API's that contain vertex classes? Or am I doing something wrong?

 

Dictionary<XYZ, bool> prevPnts =newDictionary<XYZ, bool>();
prevPnts[new XYZ(1,1,1)]=true;

// Always says the pnt doesnt exist?
if(prevPnts.ContainsKey(new XYZ(1,1,1)))
TaskDialog.Show("Contains");
elseTaskDialog.Show("NOT Contains");

 

0 Likes
637 Views
3 Replies
Replies (3)
Message 2 of 4

Anonymous
Not applicable

Ok looks like the only solution is to make my own IEqualityComparer:

 

         class XYZEqualityComparer : IEqualityComparer<XYZ>
        {
            public bool Equals(XYZ a, XYZ b)
            {
                if (Math.Abs(a.DistanceTo(b)) <= 0.05)
                    return true;
                
                return false;
            }


            public int GetHashCode(XYZ x)
            {
                int hash = 17;
                hash = hash * 23 + x.X.GetHashCode();
                hash = hash * 23 + x.Y.GetHashCode();
                hash = hash * 23 + x.Z.GetHashCode();
                return hash;
            }
        }

Dictionary<XYZ, bool> prevPnts = new Dictionary<XYZ, bool>(new XYZEqualityComparer());

 

0 Likes
Message 3 of 4

Anonymous
Not applicable

Looks like you've got the best remedy there, nice work.

 

How is it performing on real world data though?

 

I ask because your hash algorithm is working on the output from the default dotNet hashing algorithm which is not likely to work unless you are checking the exact same values from the same source (i.e. add end points found in a CurveLoop into dictionary then check subsequent points found in same loop against dictionary). If you were to gather your points and then check points found in some other unrelated (but visually co-located) geometry you will likely miss some hits.

 

A quick test indicates that doubles that are equal to around 18 significant digits or more will usually generate the same hash but for anything with less precision you will likely get different values and therefore no hits in the dictionary.

 

Revit calls anything equal down to 9 decimal places good enough and so is likely to give you XYZ points that it would report as being co-located but would generate completely different hashes using your algorithm.

 

Paste this into a fresh console app and you'll see what I mean.

 

 

using System;
 
namespace DoubleHashingTest
{
	class Program
	{
		static void Main(string[] args)
		{
			double a = 224.5000000000;
			double b = 224.5000000001;
 
			Console.WriteLine("Almost Equal?: " + (Math.Abs(a - b) <= 1.0e-9).ToString());
			
			Console.WriteLine("Hash Code A: " + a.GetHashCode().ToString());
			Console.WriteLine("Hash Code B: " + b.GetHashCode().ToString());
			
			Console.WriteLine("Hash Code A (Rounded): " + Math.Round(a,9).GetHashCode().ToString());
			Console.WriteLine("Hash Code B (Rounded): " + Math.Round(b, 9).GetHashCode().ToString());
 
			Console.ReadLine();
		}
	}
}

 

I'd suggest either rounding the components of each XYZ before adding to dictionary or simply modifying your hash function to round each component to something like 4 decimal places (depending on the accuracy you need) before generating its hash.

 

public int GetHashCode(XYZ x)
            {
                int hash = 17;
                hash = hash * 23 + Math.Round(x.X,4).GetHashCode();
                hash = hash * 23 + Math.Round(x.Y,4).GetHashCode();
                hash = hash * 23 + Math.Round(x.Z,4).GetHashCode();
                return hash;
            }

 

0 Likes
Message 4 of 4

jeremytammik
Autodesk
Autodesk

Plese also see the same topic raised on stackoverflow:

 

http://stackoverflow.com/questions/21245144/dictionary-says-key-doesnt-exist-when-it-does/29490262

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes