-1

I am checking if a total group of edges already contains the connection between 2 points.

I want to use HashSet's that will contain 2 vectors as Dictionary keys. Then I want to be able to call a performant Dictionary.ContainsKey(hashSet). I want the contains/equality check to be dependent on the Vectors in the Set.

Fex. If I add HashSet [V000 V001] to the Dict. I want to get Dictionary.ContainsKey(HashSet [V001 V000]) return true. (HashSet, so the order can vary, just the same Elements)

The Problem seems to be, that the Dictionary.ContainsKey() method does see separately created HashSets as different objects, even though, they contain the same elements.

Dictionary<HashSet<Vector3>, Vector3> d = new Dictionary<HashSet<Vector3>, Vector3>();

HashSet<Vector3> s = new HashSet<Vector3>();
s.Add(Vector3.one);
s.Add(Vector3.zero);

d.Add(s);

HashSet<Vector3> s2 = new HashSet<Vector3>();
s2.Add(Vector3.zero);
s2.Add(Vector3.one);

bool doesContain = d.ContainsKey(s2); // should be true

You also may suggest a better way of doing this 'Contains()' check efficiently.

Mechandrius
  • 323
  • 2
  • 12
  • 1
    You need a new type to use as the Dictionary key. The new type will have a Hashset property and overload `.Equals()` and `GetHashCode()` (and therefore may as well implement `IEquatable`) to do the comparison you want. The `HashSet` type doesn't provide the equality comparison you want out of the box. It only does reference equality. – Joel Coehoorn Oct 08 '19 at 13:50
  • Don't downvote my question, just because I am new here ... very unpolite. @JoelCoehoorn This seems a very good approach, but I dont fully understand where I need to override those things. Could you give a small code example? – Mechandrius Oct 08 '19 at 13:53
  • 1
    Dictionary has a constructor that accepts a custom IEqualityComparer: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.-ctor?view=netframework-4.8#System_Collections_Generic_Dictionary_2__ctor_System_Collections_Generic_IEqualityComparer__0__ – David Browne - Microsoft Oct 08 '19 at 14:02
  • 3
    @Mechandrius The question was downvoted because you didn't show any effort, or research, on your part to solve this problem. If you're new here I really recommend reading through all of the help sections, and also read Jon Skeet's [Writing the perfect question](https://codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question/). Ensuring your question is as good as it can possibly be will help you get more favourable results. – ProgrammingLlama Oct 08 '19 at 14:06
  • 1
    @DavidBrowne-Microsoft Ooh, I hadn't noticed that one before. That can be useful. – Joel Coehoorn Oct 08 '19 at 14:06
  • @John I have overworked the question and improved several unclear details. – Mechandrius Jul 31 '20 at 08:33

2 Answers2

2

The HashSet type doesn't do the equality comparison you want out of the box. It only has reference equality.

To get what you want, you'll need a new type to use as the Dictionary key. The new type will have a HashSet property, and overload Equals() and GetHashCode(), and may as well implement IEquatable at this point as well.

I'll get you started:

public class HashKey<T> : IEquatable<HashKey<T>>
{
    private HashSet<T> _items;
    public HashSet<T> Items
    {
        get {return _items;}
        private set {_items = value;}
    }

    public HashKey()
    {
        _items = new HashSet<T>();
    }
    public HashKey(HashSet<T> initialSet)
    {
        _items = initialSet ?? new HashSet();
    }

    public override int GetHashCode()
    {
        // I'm leaving this for you to do
    }

    public override bool Equals(Object obj)
    {
        if (! (obj is HashKey)) return false;
        return this.GetHashCode().Equals(obj.GetHashCode());
    }

    public bool Equals(HashSet<T> obj)
    {
        if (obj is null) return false;
        return this.GetHashCode().Equals(obj.GetHashCode());
    }
}      
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • One thing I would point out is that generally speaking it is not a good idea to use `GetHashCode` as a an equality comparison (although I am also very guilty of falling into using it) as it is true, equal objects __must__ return an equal hash code, it does not follow that objects having equal hash codes are equal. Eric Lippert has a [good article](https://blogs.msdn.microsoft.com/ericlippert/2011/02/28/guidelines-and-rules-for-gethashcode/) dealing with `GetHashCode` and makes a passing mention to this fact (pigeon hole principle). – pstrjds Oct 08 '19 at 14:13
  • @pstrjds Agreed generally.. but in this case, it's literally the only reason for this type to exist. Of course, there's always scope/feature creep later. – Joel Coehoorn Oct 08 '19 at 14:14
  • 1
    I can see that and agree with it :) Just that as OP is a student I wanted to be sure it was mentioned that in general this is not "best practice". – pstrjds Oct 08 '19 at 14:16
  • This looks like the answer. But .. It feels a bit hacky, even though, I dont understand the problem deeply. Thanks for the answers. – Mechandrius Oct 09 '19 at 09:14
1

You want to use a hashset as key.

So the keys are references where one key is one hashset reference.

The ContainsKey compare references.

For what you want to do, you can create a class that implements IEqualityComparer to pass it to the dictionary constructor.

https://learn.microsoft.com/dotnet/api/system.collections.generic.iequalitycomparer-1

If you want a full management, you should create a new class embedding the dictionary and implement your own public operations wrapping that of the dictionary : ContainsKey and all others methods you need.

public class MyDictionary : IEnumerable<>
{

  private Dictionary<HashSet<Vector3>, Vector3> d 
    = new Dictionary<HashSet<Vector3>, Vector3>();

  public int Count { get; }

  public this...

  public ContainsKey()
  {
    // implements your own comparison algorithm
  }

  public Add();

  public Remove();

  ...

}

So you will have a strongly typed dictionary for your intended usage.

  • Both solutions are good. But using a wrapped comparable HashSet key looks very simple and fitting. Rewriting the entire Dictionary could be useful when more special properties are desired. – Mechandrius Aug 19 '20 at 08:29