Variant.Hash: This is a bug right?

According to the docs:

Hash As Integer

Returns a hash value for the Variant, i.e., one that will always be the same for any Variant with the same value.

For objects, it is guaranteed to be unique among all objects currently in existence (though once an object dies, a future object may use the same hash value).

Check this code:

Var lower As Variant = "garry"
Var upper As Variant = "GARRY"

Var lowerHash As Integer = lower.Hash // 67587839
Var upperHash As Integer = upper.Hash // 67587839

If lowerHash = upperHash Then
  // This happens as the hashes are 67587839. They should be different.
  Break
End If

I know Xojo is case-insensitive but surely the hashes for these strings should be different?

1 Like

My guess is that as lower = upper then it is ‘as designed’. Xojo considers lower and upper as the same value.

You’re probably right but at least the docs should be changed. This has bitten me really quite hard and in a nasty way in a complex project today.

You could make a case-sensitive dictionary ya know.

From:

https://documentation.xojo.com/api/language/dictionary.html#dictionary-keycomparisiondelegate

Dictionaries use hashes for their lookup functions. Because of this, string keys need to match exactly; otherwise the Dictionary will not consider them equal. The only exception to this rule is with regard to string case. The characters “a” and “A” are treated as identical in Dictionary keys because of a case-insensitive hashing function. However, non-ASCII characters such as “é” and “É” are not treated as identical in Dictionary keys, even though they are equal in a direct string comparison.

1 Like

Thanks @Greg_O.

I seem to remember that there’s a hacky way to get a case sensitive dictionary:

Var d As Dictionary = ParseJSON("{}")
3 Likes

I was about to post that. :smiley:

It’s more performant than implementing your own too

I wonder why that is?

The only thing I dislike about the ParseJSON thing is it feels like a hack that is undocumented. I worry that Xojo will suddenly just change it and then my whole app collapses.

If they suddenly change it, trust many of us to be extremely vocal about the change and broken behavior.

4 Likes

Since dictionary now has a constructor taking a key comparison delegate, you can create case insensitive dictionaries.

1 Like

Please file a documentation issue.

It was an unfortunate design decision that was likely a quick solution not fully thought through…

They should have either added a case sensitive dictionary subclass or added a boolean constructor (that defaulters to False) for case sensitivity on the dictionary class instead of special casing the JSON dictionary, even if it is just adding an internal delegate under the hood.

-Karen

2 Likes

They just need to add a new constructor(CaseSensitive As Boolean = False) directly to the Dictionary Class without any subclass overhead. That can be implemented in minutes, as they already have its basis in the ParseJSON used in

ParseJSON("{}")

And the ParseJSON would use the New Dictionary(True) under hood after.

Here’s the issue for a case sensitive dictionary: 70162.

Of note, Kim raised an almost identical issue 7 years ago so I’m not holding my breath.

Done: 70163.

1 Like

do not forget a property .IsCaseSensitive As Boolean otherwise it may cause alot of issues.
i’d go for a CaseSentiveDictionary class instead.

1 Like

Unnecessary overhead, for the same job changing a small detail, just inform the feature activation.

A better Constructor approach letting space for future extra options:

Class Dictionary        // Just a basic set to show the proposed design 

    Enum Options As Integer
        CaseInsensitive
        CaseSensitive
    End Enum

    Private IsCaseSensitive_ As Boolean = False

    Property IsCaseSensitive As Boolean

        Get
            Return IsCaseSensitive_
        End Get

        Set (value As Boolean)
            DoWhateverItNeedsToSetAChange(value)
            IsCaseSensitive_ = value
        End Set

    End Property

    Sub Constructor(options As Dictionary.Options = Dictionary.Options.CaseInsensitive)

        DoWhateverItNeedsToInit()
        IsCaseSensitive = (options = Dictionary.Options.CaseSensitive)

    End Sub

End Class

1 Like