How can I use go map with custom key comparison?

2,374 views
Skip to first unread message

Dimitris Dinodimos

unread,
Dec 14, 2011, 1:38:55 PM12/14/11
to golang-nuts
I am building an interpreter using go. The language is great! But
after two months of development using go I found out a map
limitation.
I can not find a way to have custom comparison/hashing for map keys.
In the following example I am expecting the rational number 1/2 to be
the same key for the map even if it is stored in a different address.

package main

import (
"fmt"
"big"
)

func main() {
var x map[*big.Rat]string = make(map[*big.Rat]string)
var onehalf = big.NewRat(1,2)
x[onehalf] = "this is 1/2"
var otherhalf = big.NewRat(1,2)
val, ok := x[onehalf]
fmt.Printf("val = %v, ok = %v\n", val, ok)
val, ok = x[otherhalf]
fmt.Printf("val = %v, ok = %v\n", val, ok)
}

The example code prints:

val = this is 1/2, ok = true
val = , ok = false

I am expecting:

val = this is 1/2, ok = true
val = this is 1/2, ok = true

Is there some way to overcome this problem? If not, are there any
plans for fixing this problem? (perhaps by using a Comparator
interface implemented by big.Rat).

John Asmuth

unread,
Dec 14, 2011, 2:04:08 PM12/14/11
to golan...@googlegroups.com
http://code.google.com/p/gohash/

It won't have the same syntax as the built-in map type, but it allows you to do fast retrieval of arbitrary data, assuming you can come up with a good hash function for it.

- John

Kyle Lemons

unread,
Dec 14, 2011, 2:09:35 PM12/14/11
to Dimitris Dinodimos, golang-nuts
I am building an interpreter using go. The language is great! But
after two months of development using go I found out a map
limitation.
I can not find a way to have custom comparison/hashing for map keys.
In the following example I am expecting the rational number 1/2 to be
the same key for the map even if it is stored in a different address.

The built-in map implementation is intended to be suitable and reasonably performant for common uses but not the end-all-be-all of hash maps.  Those can be implemented in a way suitable for your application, though they won't have the same syntax as a built-in map type.

peterGo

unread,
Dec 14, 2011, 3:47:09 PM12/14/11
to golang-nuts
Dimitris,

You are using confusing pointers and values. For example,

package main

import (
"fmt"
)

func main() {
var one = new(int)
var otherone = new(int)
fmt.Println(*one, *otherone, *one == *otherone)
fmt.Println(one, otherone, one == otherone)
}

Output:
0 0 true
0xf840029018 0xf840029020 false

Peter

Steven Blenkinsop

unread,
Dec 15, 2011, 4:59:02 PM12/15/11
to Dimitris Dinodimos, golang-nuts
On Wed, Dec 14, 2011 at 1:38 PM, Dimitris Dinodimos <dim...@gmail.com> wrote:
I am building an interpreter using go. The language is great! But
after two months of development using go I found out a map
limitation.
I can not find a way to have custom comparison/hashing for map keys.
In the following example I am expecting the rational number 1/2 to be
the same key for the map even if it is stored in a different address.

You could construct a key from the big.Rat. The easiest is just to use the String() representation. Or, you can skip decimal encoding the numbers and use a struct like:

type RatKey struct {
Sign  int
Num   string
Denom string
}

where Num and Denom are taken from the Bytes() representation of the corresponding Num() and Denom() of the big.Rat, and Sign is the Sign() of the big.Rat. Doing it this way is less complex than encoding the number to a decimal string representation, which is nice for when big.Rat gets, well, big.

However, in both cases, I'm not clear on whether you have to manually normalize the big.Rat first. If so, this becomes the bottleneck. I have yet to see a GcdInt return something other than 1 for a big.Rat, and the code seems to normalize the big.Rat at certain points, but its unspecified and Cmp uses a formula that would accept non-normalized numbers.
Reply all
Reply to author
Forward
0 new messages