Object of Custom Type as Dictionary Key

Object of custom type as dictionary key

You need to add 2 methods, note __hash__ and __eq__:

class MyThing:
def __init__(self,name,location,length):
self.name = name
self.location = location
self.length = length

def __hash__(self):
return hash((self.name, self.location))

def __eq__(self, other):
return (self.name, self.location) == (other.name, other.location)

def __ne__(self, other):
# Not strictly necessary, but to avoid having both x==y and x!=y
# True at the same time
return not(self == other)

The Python dict documentation defines these requirements on key objects, i.e. they must be hashable.

Use custom object as Dictionary Key

You also need to override GetHashCode() (and preferably also Equals()). Your otherwise-equal object is returning a different hash code, which means that the key is not found when looked up.

The GetHashCode() contract specifies that the return value from two objects MUST be equal when the two objects are considered equal. This is the root of your problem; your class does not meet this requirement. The contract does not specify that the value must be different if they are not equal, but this will improve performance. (If all of the objects return the same hash code, you may as well use a flat list from a performance perspective.)

A simple implementation in your case might be:

public override int GetHashCode()
{
return AValue.GetHashCode() ^ BValue.GetHashCode();
}

Note that it might be a good idea to test if AValue or BValue are null. (This will be somewhat complicated since you don't constrain the generic types A and B, so you cannot just compare the values to null -- the types could be value types, for example.)1

It's also a good idea to make classes you intend to use as dictionary keys immutable. If you change the value of an object that is being used as a key, the dictionary will exhibit weird behavior since the object is now in a bucket where it doesn't belong.


1 Note that you could make use of EqualityComparer<A>.Default.GetHashCode(AValue) (and similar for BValue) here, as that will eliminate the need for a null check.

How to use a custom class type to be the key in Dictionary in Swift?

Any custom type that you want to use a dictionary key must conform with the Hashable protocol.

This protocol has one property that you must implement.

var hashValue: Int { get }

Use this property to generate an int that Dictionary can use for lookup reasons. You should try to make it so the generated hashValue is unique for each pixel.

The Swift book has the following note, so you can probably make a random hash (as long as it's unique):

The value returned by a type's hashValue property is not required to be the same across different executions of the same program, or in different programs.

Note that because Hashable inherits from Equatable you must also implement:

func ==(_ lhs: Self, _ rhs: Self) -> Bool.

I'm not sure what the internal structure of your pixel is, but you could probably consider two pixels equal when both have the same "x" and "y" values. The final logic is up to you.

Modify this as you need:

struct Pixel : Hashable {

// MARK: Hashable
var hashValue: Int {
get {
// Do some operations to generate a unique hash.
}
}
}

//MARK: Equatable
func ==(lh: Pixel, rh: Pixel) -> Bool {
return lh.x == rh.x && rh.y == lh.y
}

How to use custom object as dictionary key?

You are missing an override in your second try:

public override bool Equals(object o)

Sample Image

How do you use a custom type for a dictionary key?

This implements IEquatable and overrides GetHashCode and Equals:

' an assumption about Pair(Of... :
Public Class Pair(Of T, TT)
Implements IEquatable(Of Pair(Of T, TT))

Public Property ValueT As T
Public Property ValueTT As TT

Then, the methods:

' basic Equals for this Type
Public Overrides Function Equals(obj As Object) As Boolean
If obj.GetType Is GetType(Pair(Of Long, Integer)) Then
Return Equals1(CType(obj, Pair(Of T, TT)))
Else
Return False
End If
End Function

' used by the Dictionary
Public Function Equals1(obj As Pair(Of T,
TT)) As Boolean Implements IEquatable(Of Pair(Of T, TT)).Equals
' the other thing is Something Else
If obj.GetType <> GetType(Pair(Of Long, Integer)) Then
Return False
End If

'prefer T over TT, testing first
If Integer.Equals(obj.ValueT, ValueT) = False Then
Return False
End If

'T is equal, what about TT:
Return Long.Equals(obj.ValueTT, ValueTT)
End Function

' dictionary will use the hashcode for ContainsKey, Add
Public Overrides Function GetHashCode() As Integer
' https://stackoverflow.com/a/371348/1070452
' marc gravell:
Dim hash As Integer = 13
hash = (hash * 7) + ValueT.GetHashCode()
hash = (hash * 7) + ValueTT.GetHashCode()

Return hash

''msdn (non generic value types):
'Dim hCode As Long = ValueT Xor ValueTT
'Return hCode.GetHashCode()
End Function

Testing:

Dim a = New Pair(Of Long, Integer)(10, 10)
Dim b = New Pair(Of Long, Integer)(5, 5)
' different object, same values:
Dim c = New Pair(Of Long, Integer)(10, 10)

Dim mydict As New Dictionary(Of Pair(Of Long, Int32), String)
mydict.Add(a, "ziggy")
mydict.Add(b, "zoey")

Console.WriteLine("a==b? {0}", a.Equals(b).ToString)
Console.WriteLine("a==c? {0}", a.Equals(c).ToString)
Console.WriteLine("b==c? {0}", b.Equals(c).ToString)

Console.WriteLine("Contains a? {0}", mydict.ContainsKey(a).ToString)
Console.WriteLine("Contains b? {0}", mydict.ContainsKey(b).ToString)
' since the c OBJECT is not in the collection, it SHOULD report false
' but since the values are, and thats all that seems to matter:
Console.WriteLine("Contains c? {0}", mydict.ContainsKey(c).ToString)

Result:

a==b? False
a==c? True
b==c? False
Contains a? True
Contains b? True
Contains c? True

The tests using c are actually false. The object 'c' was never added to the Dictionary, and 'c' is a different object than a. But the overrides used simply tests the objects based on the 2 values which are the same.

I do not think I would do this, because a<>c. Instead, maybe use a collection class of some sort and avoid redefining Equals. The exact implementation would depend on whats in the collection. This would be a last resort, for me.

See Also:

  • IEquatable
  • Why is it important to override GetHashCode when Equals method is overridden?
  • Dictionary Reference Source

Can I use an object (an instance of a class) as a dictionary key in Python?

Your instances need to be hashable. The python glossary tells us:

An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() or __cmp__() method). Hashable objects which compare equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally.

All of Python’s immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are. Objects which are instances of user-defined classes are hashable by default; they all compare unequal, and their hash value is their id().

Using the field of an object as a generic Dictionary key

By default, the two important methods are GetHashCode() and Equals(). It is important that if two things are equal (Equals() returns true), that they have the same hash-code. For example, you might "return FooID;" as the GetHashCode() if you want that as the match. You can also implement IEquatable<Foo>, but that is optional:

class Foo : IEquatable<Foo> {
public string Name { get; set;}
public int FooID {get; set;}

public override int GetHashCode() {
return FooID;
}
public override bool Equals(object obj) {
return Equals(obj as Foo);
}
public bool Equals(Foo obj) {
return obj != null && obj.FooID == this.FooID;
}
}

Finally, another alternative is to provide an IEqualityComparer<T> to do the same.

Using object as key in dictionary in Python - Hash function

Elements of a mapping are not accessed by their hash, even though their hash is used to place them within the mapping. You must use the same value when indexing both for storage and for retrieval.



Related Topics



Leave a reply



Submit