I have a recursive function that creates objects representing file paths (the keys are paths and the values are info about the file). It's recursive as it's only meant to handle files, so if a directory is encountered, the function is recursively called on the directory.
All that being said, I'd like to do the equivalent of a set union on two maps (i.e. the "main" map updated with the values from the recursive call). Is there an idiomatic way to do this aside from iterating over one map and assigning each key, value in it to the same thing in the other map?
That is: given a,b are of type map [string] *SomeObject, and a and b are eventually populated, is there any way to update a with all the values in b?
There is no built in way, nor any method in the standard packages to do such a merge.
The idomatic way is to simply iterate:
for k, v := range b {
a[k] = v
}
Since Go 1.18, you can simply use the Copy function from the golang.org/x/exp/maps package:
package main
import (
"fmt"
"golang.org/x/exp/maps"
)
func main() {
src := map[string]int{
"one": 1,
"two": 2,
}
dst := map[string]int{
"two": 2,
"three": 3,
}
maps.Copy(dst, src)
fmt.Println("src:", src)
fmt.Println("dst:", dst)
}
(Playground)
Output:
src: map[one:1 two:2]
dst: map[one:1 three:3 two:2]
One caveat of this approach is that, in Go versions 1.18.x to 1.19.x, your map's key type must be concrete, i.e. not an interface type. For instance, the compiler won't allow you to pass values of type map[io.Reader]int to the Copy function:
package main
import (
"fmt"
"io"
"golang.org/x/exp/maps"
)
func main() {
var src, dst map[io.Reader]int
maps.Copy(dst, src)
fmt.Println("src:", src)
fmt.Println("dst:", dst)
}
(Playground)
Compiler output:
go: finding module for package golang.org/x/exp/maps
go: downloading golang.org/x/exp v0.0.0-20220328175248-053ad81199eb
./prog.go:12:11: io.Reader does not implement comparable
Go build failed.
This limitation was lifted in Go 1.20.
(Playground)
Starting at go 1.18, thanks to the release of the Generics feature, there are now generic functions that union maps!
You can use a package like https://github.com/samber/lo in order to do so.
Note that the key can be of any "comparable" type, while the value can be of any type.
Example:
package main
import (
"fmt"
"github.com/samber/lo"
)
func main() {
map1 := map[string]interface{}{"k1": "v1", "k2": 2}
map2 := map[string]interface{}{"k2": "v2new", "k3": true}
map1 = lo.Assign(map1, map2)
fmt.Printf("%v", map1)
}
The result is:
map[k1:v1 k2:v2new k3:true]
If you have a couple of nested maps, left and right, then this function will recursively add the items from right into left. If the key is already in left then we recurse deeper into the structure and attempt only add keys to left (e.g. never replace them).
type m = map[string]interface{}
// Given two maps, recursively merge right into left, NEVER replacing any key that already exists in left
func mergeKeys(left, right m) m {
for key, rightVal := range right {
if leftVal, present := left[key]; present {
//then we don't want to replace it - recurse
left[key] = mergeKeys(leftVal.(m), rightVal.(m))
} else {
// key not in left so we can just shove it in
left[key] = rightVal
}
}
return left
}
NOTE: I do not handle the case in which the value is not itself a map[string]interface{}. So if you have left["x"] = 1 and right["x"] = 2 then the above code will panic when attempting leftVal.(m).
Here is another option,
in case you are trying to limit the number of third-party dependencies such github.com/samber/lo, OR
you are not comfortable with the experimental nature of golang.org/x/exp (read the warning), OR
you would rather the convenience of an append()-like API instead of exp.Copy() from golang.org/x/exp (append accepts any number of lists, whereas Copy() accepts only 2).
However it requires Go 1.18+ as it uses go generics.
Save the following in one of your modules/packages:
func MergeMaps[M ~map[K]V, K comparable, V any](src ...M) M {
merged := make(M)
for _, m := range src {
for k, v := range m {
merged[k] = v
}
}
return merged
}
Then you can use it very similarly to append():
func main() {
mergedMaps := MergeMaps(
map[string]int{"a": 1, "b": 2},
map[string]int{"b": 3, "c": 4},
map[string]int{"c": 3, "d": 4},
)
fmt.Println(mergedMaps)
}
Go is limited by what type of map it is. I'd suspect that there isn't built in functions because of the infinite number of type declarations that could exist for a map. So you have to build your own Merge functions depending on what type of map you are using:
func MergeJSONMaps(maps ...map[string]interface{}) (result map[string]interface{}) {
result = make(map[string]interface{})
for _, m := range maps {
for k, v := range m {
result[k] = v
}
}
return result
}
Related
Here is the simplified code of a cache. Suppose Container placed in a package, so it don't know about Member.
While I wanna store instances of Member in Container, So I store an empty instance of Member in Container as outerType.
In the Container->GetMysql, I fill a new variable by test values (but, in real world, It fill by data of database, dynamically).
then in the function Put, I store data in items as Cache for next uses. In the Get I get the data stored in the items.
Before this every thing is fine. My problem is where i want to convert result of Get to type of Member m = res.(Member) . How Can I convert it to an instance of Member
I found many question about this subject, but none of them solved my problem
For more detail: I want the Get return data with its pointer of where it stored in items. So if I get some variable of same member, an change in one are shown in others
package main
import (
"fmt"
"reflect"
)
type Member struct {
Id int
Name string
Credit int
Age int
}
type Container struct {
outerType interface{}
items map[string]*interface{}
}
func (cls *Container)GetMysql(s string, a int64) interface{}{
obj := reflect.New(reflect.TypeOf(cls.outerType))
elem := obj.Elem()
//elem := reflect.ValueOf(o).Elem()
if elem.Kind() == reflect.Struct {
f := elem.FieldByName("Name")
f.SetString(s)
f = elem.FieldByName("Credit")
f.SetInt(a)
}
return obj.Interface()
}
func (cls *Container)Get(value string) *interface{}{
return cls.items[value]
}
func (cls *Container)Put(value string, a int64) {
res := cls.GetMysql(value, a)
cls.items[value] = &res
}
func main() {
c := Container{outerType:Member{}}
c.items = make(map[string]*interface{})
c.Put("Jack", 500)
res := c.Get("Jack")
fmt.Println(*res)
m := &Member{}
m = res.(Member) // Here is the problem. How to convert ?
fmt.Println(m)
}
You should hardly ever use pointer to interface. My advice is to never use it, when you'll need it, you'll know.
Instead if you need a pointer to something (so you can have the same pointer at multiple places, and so modifying the pointed value somewhere, it will have effect on the others), "wrap the pointer" in the interface value.
So first modify the items field so that it stores interface{} values instead of pointers:
items map[string]interface{}
This means no restriction: you can pass and store pointers, that's not a problem.
Next modify Get() to return interface{}:
func (cls *Container) Get(value string) interface{}{
return cls.items[value]
}
And also in Put(), don't take the address of an interface{}:
func (cls *Container) Put(value string, a int64) {
res := cls.GetMysql(value, a)
cls.items[value] = res
}
And you have to type-assert *Member from the values returned by Get().
And now testing it:
c := Container{outerType: Member{}}
c.items = make(map[string]interface{})
c.Put("Jack", 500)
res := c.Get("Jack")
fmt.Println(res)
m := res.(*Member) // Here is the problem. How to convert ?
fmt.Println(m)
Output (try it on the Go Playground):
&{0 Jack 500 0}
&{0 Jack 500 0}
Now if you would modify a field of m:
m.Credit = 11
And then get the value form the cache:
fmt.Println(c.Get("Jack"))
We'll see the modified value, even though we did not call Put() (try it on the Go Playground):
&{0 Jack 11 0}
This question already has an answer here:
Why does append() modify the provided slice? (See example)
(1 answer)
Closed 4 years ago.
I tried to add an element to my slice inside a function. I can change the element of the slice but cannot add a new element to it. Since slices act like reference why can't I change it?
Below is the code I have tried:
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3}
change(a)
fmt.Println(a)
}
func change(a []int) {
a[0] = 4
a = append(a, 5)
}
Slice are pointers to underlying array. It is described in Golang:
Map and slice values behave like pointers: they are descriptors that
contain pointers to the underlying map or slice data. Copying a map or
slice value doesn't copy the data it points to. Copying an interface
value makes a copy of the thing stored in the interface value. If the
interface value holds a struct, copying the interface value makes a
copy of the struct. If the interface value holds a pointer, copying
the interface value makes a copy of the pointer, but again not the
data it points to.
you are passing a copy of the slice not the original slice. Return the value after appending to the slice and then assign it to the original slice as
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3}
a = change(a)
fmt.Println(a)
}
func change(a []int) []int{
a = append(a, 5)
return a
}
Playground Example
Or you can pass a pointer to slice of int but it is not recommended since slice it self is a pointer to bootstrap array.
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3}
change(&a)
fmt.Println(a)
}
func change(a *[]int){
*a = append(*a, 5)
}
Note: Everything in Golang is pass by value.
One thing to be considered is even if you are returning the updated slice and assigning to the same value, its original len and cap will change, which will lead to a new underlying array of different len. Try to print the length and cap before and after changing the slice to see the difference.
fmt.Println(len(a), cap(a))
The length is the number of elements referred to by the slice. The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer).
Since the underlying array will check you can check it using reflect and unsafe for fetching the underlying array which is going to be different if cap of a slice change after appending data which is your case.
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := []int{1, 2, 3}
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&a))
data := *(*[3]int)(unsafe.Pointer(hdr.Data))
fmt.Println(data)
a = change(a)
hdr = (*reflect.SliceHeader)(unsafe.Pointer(&a))
newData := *(*[4]int)(unsafe.Pointer(hdr.Data))
fmt.Println(newData)
}
func change(a []int) []int {
a = append(a, 5)
return a
}
Playground Example
This is the best part of slices that you need to worry about its capacity when appending data more than its capacity, since it will point to a new array allocated in the memory of bigger length.
I am writing a function that iterates over the entries in a map. I want to be able to deal cleanly with items which are added or deleted from the map while iterating, like for k, v := range myMap { //... does, but I am processing just one key/value pair per iteration so I can't use range. I want something like:
func processItem(i iterator) bool {
k, v, ok := i.next()
if(!ok) {
return false
}
process(v)
return true
}
var m = make(map[string]widget)
// ...
i := makeIterator(m)
for processItem(i) {
// code which might add/remove item from m here
}
I know that range is using a 'hiter' struct and associated functions, as defined in src/runtime/hashmap.go, to perform iteration. Is there some way to gain access to this iterator as a reified (first-class) Go object?
Is there an alternative strategy for iterating over a map which would deal well with insertions/deletions but give a first-class iterator object?
Bonus question: is there an alternative strategy for iterating over a map which could also deal with the map and iterator being serialised to disk and then restored, with iteration continuing from where it left off? (Obviously the built-in range iterator does not have this capability!)
You can't :(
The only way to iterate over a map is by using for range and you can't get an iterator object out of that.
You can use channels as iterators.
Your iterator would be a function returning a channel that communicates the current iteration value to whoever receives it:
func iterator(m map[string]widget) chan iteration {
c := make(chan iteration)
go func() {
for k,v := range m {
c <- iteration{k,v}
}
close(c)
}()
return c
}
This is of course not generic, you could make it generic using interface{} and/or reflection but that shouldn't be too hard if you actually need it.
Closing the channel at the end of iteration will notify the end of iteration, demonstrated later.
The iteration type is just there so you can send key and value at the same time, it would look something like this:
type iteration struct {
key string
value widget
}
With this you can then do this (on play):
m := map[string]widget{"foo": widget{3}, "bar": widget{4}}
i := iterator(m)
iter, ok := <- i
fmt.Println(iter, ok)
iter, ok = <- i
fmt.Println(iter, ok)
iter, ok = <- i
fmt.Println(iter, ok)
which yields
{foo {3}} true
{bar {4}} true
{ {0}} false
A very simple approach is to obtain a list of all the keys in the map, and package the list and the map up in an iterator struct. When we want the next key, we take the next one from the list that hasn't been deleted from the map:
type iterator struct {
m map[string]widget
keys []string
}
func newIterator(m map[string]widget) *iterator {
it := iterator{m, make([]string, len(m))}
i := 0
for k, _ := range m {
it.keys[i] = k
i++
}
return &it
}
func (it *iterator) next() (string, widget, bool) {
for len(it.keys) > 0 {
k := it.keys[0]
it.keys = it.keys[1:]
if _, exists := it.m[k]; exists {
return k, it.m[k], true
}
}
return "", widget{0}, false
}
See running example on play.
You can define your own map type. Also it will be good to solve concurrency problem:
type ConcurrentMap struct {
sync.RWMutex
items map[string]interface{}
}
type ConcurrentMapItem struct {
Key string
Value interface{}
}
func (cm *ConcurrentMap) Iter() <-chan ConcurrentMapItem {
c := make(chan ConcurrentMapItem)
f := func() {
cm.Lock()
defer cm.Unlock()
for k, v := range cm.items {
c <- ConcurrentMapItem{k, v}
}
close(c)
}
go f()
return c
}
As I understand it, I cannot define equality for user-defined types in Go. So what would be the idiomatic way of computing the number of distinct objects of some custom type (possibly recursively defined). Here is an example of the kind of thing I am trying to do.
package main
import "fmt"
type tree struct {
left *tree
right *tree
}
func shapeOf(a tree) string {
temp := "{"
if a.left != nil {
temp += shapeOf(*(a.left))
}
temp += "}{"
if a.right != nil {
temp += shapeOf(*(a.right))
}
temp += "}"
return temp;
}
func main() {
a := tree{nil, nil}
b := tree{nil, &a}
c := tree{nil, nil}
d := tree{nil, &c}
e := tree{nil, nil}
f := tree{&e, nil}
s := make(map[string]bool)
s[shapeOf(b)] = true
s[shapeOf(d)] = true
s[shapeOf(f)] = true
fmt.Println(len(s)) // As required, prints 2 because the first two trees have the same shape
}
It works, but the use of strings is extremely ugly, and probably inefficient too. Obviously I could easily write a recursive method to tell if two trees are equal - something like
func areEqual(a, b tree) bool
but this wouldn't enable me to use trees as map keys. What is the idiomatic Go way to do something like this?
You cannot define equality for user-defined type because it is already defined by go. Basically, all there is to know about it is explained in the comparable section.
Short story: two struct values can be compared if their fields can be compared (no slice, map or function). And same thing for equality: two structs are equal if their fields are equal. In your case, the problem is that for comparing pointers, Golang compares the memory addresses, not the struct they point to.
So, is this possible to count distinct values of a certain struct ? Yes, if the struct contain no nested slice, map, function or pointer. For recursive types, that's not possible because you cannot define something like this:
type tree struct {
left tree
right tree
}
The idiomatic way of testing the equality of recursive types is to use reflect.DeepEqual(t1, t2 interface{}) as it follows indirections. However, this method is inefficient because uses heavy reflection. In your case, I do not think there is any clean and elegant solution to get what you want.
To be short, here is a deal:
http://play.golang.org/p/ePiZcFfPZP
If I use commented lines, everything works, but there is no
any control on allocation sizes (cap), so the slices,
if I got it correct, realloc every time they exceed their limit
and moreover, they start with zero capacity.
Passing a reference of newSlice in setSlice() don't work too.
So, I need ideomatic, elegant, go-ish method to do the job.
Thanks in advance, at least for attention and your time.
UPD:
solution was to make SLICE and STASH *[]byte typed
and make assigns to them like:
var slicePtr *[]byte
tmp := make([]byte, 256)
slicePtr = &tmp // Tmp is needed because we can't take adress of make() rval.
For example,
package main
import "fmt"
var SLICE, STASH []byte
func init() {
SLICE = make([]byte, 0, 5)
}
func setSlice(slice []byte) {
STASH = SLICE
SLICE = slice
}
func restoreSlice() {
SLICE = STASH
}
func appendToSlice(parts ...byte) []byte {
SLICE = append(SLICE, parts...)
return SLICE
}
func main() {
appendToSlice('f', 'o', 'o')
fmt.Printf("Everything is fine: {'%s'}\n", SLICE)
newSlice := make([]byte, 0, 5)
setSlice(newSlice)
newSlice = appendToSlice('b', 'a', 'r')
fmt.Printf("Bar? No! {'%s'}\n", newSlice) // <- I need "bar" appear in newSlice.
fmt.Printf("Bar is here: {'%s'}\n", SLICE)
restoreSlice()
fmt.Printf("Back to origin. {'%s'}\n", SLICE)
}
Output:
Everything is fine: {'foo'}
Bar? No! {'bar'}
Bar is here: {'bar'}
Back to origin. {'foo'}
Like the Go append built-in function, your appendToSlice function needs to return the result of the append.
func appendToSlice(parts ...byte) []byte {
SLICE = append(SLICE, parts...)
return SLICE
}
and
newSlice = appendToSlice('b', 'a', 'r')
The Go Programming Language Specification
Appending to and copying slices
The built-in functions append and copy assist in common slice
operations. For both functions, the result is independent of whether
the memory referenced by the arguments overlaps.
The variadic function append appends zero or more values x to s of
type S, which must be a slice type, and returns the resulting slice,
also of type S.
If the capacity of s is not large enough to fit the additional values,
append allocates a new, sufficiently large underlying array that fits
both the existing slice elements and the additional values. Otherwise,
append re-uses the underlying array.
Example:
var b []byte
b = append(b, "bar"...) // append string contents; b == []byte{'b', 'a', 'r' }