Alternative to using map as key of map in GO - go

I want to tag my maps with a timestamp, there will be various maps - dynamically generated. The keys will be repeated, so if the same map appears I want to updated its values
I'm getting the maps from external sources and want to registered when they are read
Ever so often I want to iterate the map and find those maps that didn't update for a while
a = map[string]string{"a":1, "b":2}
b = map[map[string]string]time.Time{}
b[a] = 1
I understand it's not supported but I was wondering what are the some ways of doing it?
The first thing that comes to mind is: serialize the map into string and use it as key,,?

Make a struct that will keep the info you need:
type MyMap struct {
timestamp time.Time
m map[string]string
}
And lookup by iterating over a slice of such structs:
func lookup(needle map[string]string, haystack []MyMap) (MyMap, bool) {
for _, myMap := range haystack {
if reflect.DeepEqual(needle, myMap.m) {
return myMap, true
}
}
return nil, false
}
Alternatively, convert your map to JSON and use the result as the key:
b, _ := json.Marshal(myMap)
key := string(b)
m, ok := myMaps[key] // myMaps is map[string]MyMap
if !ok {
myMaps[key] = myMap
}
Apparently, json.Marshal sorts the map's keys before marshaling, so this should be a reliable solution.

Related

How can I map a list of integers to an integer in golang? [duplicate]

Is it possible to use slices as keys?
There is my attempt:
h := map[[]string]string{
[]string{"a", "b"} : "ab",
}
the compiler gives me an error invalid map key type []string. So either it's not possible or I declared it incorrectly (if so, what would be a correct way?).
However, it is possible to use arrays as map keys:
package main
import "fmt"
func main() {
m := make(map[[2]int]bool)
m[[2]int{1, 2}] = false
fmt.Printf("%v", m)
}
No, slices cannot be used as map keys as they have no equality defined.
Volker already told that this is not possible and I will give a little bit more details of why is it so with examples from the spec.
Map spec tells you:
The comparison operators == and != must be fully defined for operands
of the key type; thus the key type must not be a function, map, or
slice.
It already tells you that the slice can't be a key, but you could have checked it also in the comparison spec:
Slice, map, and function values are not comparable.
This means that also slice can't be a key, an array can be a key. For example you can write:
h := map[[2]string]string{
[2]string{"a", "b"} : "ab",
}
Depending on your requirements and the complexity of your data, you could use a string as a map key and then use a hash of your slice as the map key.
The nice thing is you can use this technique with anything that can be converted to or from a slice of bytes.
Here's a quick way to convert your slice of strings into a slice of bytes:
[]byte(strings.Join([]string{},""))
Here's an example using SHA1:
type ByteSliceMap struct {
buf *bytes.Buffer
m map[string][]byte
}
func (b *ByteSliceMap) key(buf []byte) string {
h := sha1.New()
h.Write(buf)
sum := h.Sum(nil)
return fmt.Sprintf("%x", sum)
}
func (t *ByteSliceMap) value(key []byte) (value []byte, ok bool) {
value, ok = t.m[t.key(key)]
return
}
func (t *ByteSliceMap) add(key, value []byte) {
if t.m == nil {
t.m = make(map[string][]byte)
}
t.m[t.key(key)] = value
}
Working version
One way to get around this problem is to actually create a key from a slice which has well defined comparison operators:
func createKey(s []string) string { return fmt.Sprintf("%q", s) }
m := make(map[string]string)
s := []string{"a","b"}
m[createKey(s)] = "myValue"
In a similar fashion you would have to create functions for creating keys of slices with type different to string.

Want to take a map to a slice structure

I have a map with a key and a value structure that i want to put into a simple slice.
The struct I want to store the values as ,
type Attribute struct {
AttId container.AttrID
AttMess []json.RawMessage
}
The current loop I have is something like this which takes the keys from the existing map,
keys := make([]container.AttrID, 0, len(AttId))
for k := range AttId {
keys = append(keys, k)
}
for _, k := range keys {
fmt.Println(k, AttId[k])
}
How would I construct a slice that holds the keys and values inside attributes with the struct above? I'm a bit lost if you can actually make this.
Thanks!
You should simply range over the map and construct instances of the struct. Assuming the map values are []json.RawMessage types:
attrs:=make([]Attribute,0,len(attributes))
for k,v:=range attributes {
attrs=append(attrs,Attribute{AttributesId:k, AttributesMessage:v})
}

how do i create a general function to receive a map in go lang?

how can i pass map data in to a general function (isExist) to check, the given value is exist or not
passing map type may be map[int]int or map[string]string or any
func IsExist(text int, data map[interface{}]interface{}) bool {
for key, _ := range data {
if data[key] == text {
return true
}
}
return false
}
func main() {
var data = make(map[string]int)
//var data =map[interface {}]interface{} this case will working fine
data["a"] = 1
data["b"] = 2
fmt.Println(IsExist(2, data))
//throwing error that 'cannot use data (type map[string]int) as type map[interface {}]interface {} in argument to IsExist'
}
please let me know how can you generalize it?
Go's type system can't do that. map[string]int and map[interface {}]interface{} are distinct types. Besides, storage/representation of objects in memory depends on the type. A string has one memory layout. But the very same string, packaged as a interface{} - totally different. For the same reasons you can't "cast" a []int to a []interface{}. You have to copy the elements into the new slice.
Same here. Either make your map a map[interface {}]interface{} from the start, or copy all key-values pairs to one, prior to the call of the generic function.

Can we write a generic array/slice deduplication in go?

Is there a way to write a generic array/slice deduplication in go, for []int we can have something like (from http://rosettacode.org/wiki/Remove_duplicate_elements#Go ):
func uniq(list []int) []int {
unique_set := make(map[int] bool, len(list))
for _, x := range list {
unique_set[x] = true
}
result := make([]int, len(unique_set))
i := 0
for x := range unique_set {
result[i] = x
i++
}
return result
}
But is there a way to extend it to support any array? with a signature like:
func deduplicate(a []interface{}) []interface{}
I know that you can write that function with that signature, but then you can't actually use it on []int, you need to create a []interface{} put everything from the []int into it, pass it to the function then get it back and put it into a []interface{} and go through this new array and put everything in a new []int.
My question is, is there a better way to do this?
While VonC's answer probably does the closest to what you really want, the only real way to do it in native Go without gen is to define an interface
type IDList interface {
// Returns the id of the element at i
ID(i int) int
// Returns the element
// with the given id
GetByID(id int) interface{}
Len() int
// Adds the element to the list
Insert(interface{})
}
// Puts the deduplicated list in dst
func Deduplicate(dst, list IDList) {
intList := make([]int, list.Len())
for i := range intList {
intList[i] = list.ID(i)
}
uniques := uniq(intList)
for _,el := range uniques {
dst.Insert(list.GetByID(el))
}
}
Where uniq is the function from your OP.
This is just one possible example, and there are probably much better ones, but in general mapping each element to a unique "==able" ID and either constructing a new list or culling based on the deduplication of the IDs is probably the most intuitive way.
An alternate solution is to take in an []IDer where the IDer interface is just ID() int. However, that means that user code has to create the []IDer list and copy all the elements into that list, which is a bit ugly. It's cleaner for the user to wrap the list as an ID list rather than copy, but it's a similar amount of work either way.
The only way I have seen that implemented in Go is with the clipperhouse/gen project,
gen is an attempt to bring some generics-like functionality to Go, with some inspiration from C#’s Linq and JavaScript’s underscore libraries
See this test:
// Distinct returns a new Thing1s slice whose elements are unique. See: http://clipperhouse.github.io/gen/#Distinct
func (rcv Thing1s) Distinct() (result Thing1s) {
appended := make(map[Thing1]bool)
for _, v := range rcv {
if !appended[v] {
result = append(result, v)
appended[v] = true
}
}
return result
}
But, as explained in clipperhouse.github.io/gen/:
gen generates code for your types, at development time, using the command line.
gen is not an import; the generated source becomes part of your project and takes no external dependencies.
You could do something close to this via an interface. Define an interface, say "DeDupable" requiring a func, say, UniqId() []byte, which you could then use to do the removing of dups. and your uniq func would take a []DeDupable and work on it

Slice as a key in map

Is it possible to use slices as keys?
There is my attempt:
h := map[[]string]string{
[]string{"a", "b"} : "ab",
}
the compiler gives me an error invalid map key type []string. So either it's not possible or I declared it incorrectly (if so, what would be a correct way?).
However, it is possible to use arrays as map keys:
package main
import "fmt"
func main() {
m := make(map[[2]int]bool)
m[[2]int{1, 2}] = false
fmt.Printf("%v", m)
}
No, slices cannot be used as map keys as they have no equality defined.
Volker already told that this is not possible and I will give a little bit more details of why is it so with examples from the spec.
Map spec tells you:
The comparison operators == and != must be fully defined for operands
of the key type; thus the key type must not be a function, map, or
slice.
It already tells you that the slice can't be a key, but you could have checked it also in the comparison spec:
Slice, map, and function values are not comparable.
This means that also slice can't be a key, an array can be a key. For example you can write:
h := map[[2]string]string{
[2]string{"a", "b"} : "ab",
}
Depending on your requirements and the complexity of your data, you could use a string as a map key and then use a hash of your slice as the map key.
The nice thing is you can use this technique with anything that can be converted to or from a slice of bytes.
Here's a quick way to convert your slice of strings into a slice of bytes:
[]byte(strings.Join([]string{},""))
Here's an example using SHA1:
type ByteSliceMap struct {
buf *bytes.Buffer
m map[string][]byte
}
func (b *ByteSliceMap) key(buf []byte) string {
h := sha1.New()
h.Write(buf)
sum := h.Sum(nil)
return fmt.Sprintf("%x", sum)
}
func (t *ByteSliceMap) value(key []byte) (value []byte, ok bool) {
value, ok = t.m[t.key(key)]
return
}
func (t *ByteSliceMap) add(key, value []byte) {
if t.m == nil {
t.m = make(map[string][]byte)
}
t.m[t.key(key)] = value
}
Working version
One way to get around this problem is to actually create a key from a slice which has well defined comparison operators:
func createKey(s []string) string { return fmt.Sprintf("%q", s) }
m := make(map[string]string)
s := []string{"a","b"}
m[createKey(s)] = "myValue"
In a similar fashion you would have to create functions for creating keys of slices with type different to string.

Resources