Merge Maps in Golang - dictionary

I need to merge multiple maps map1 = [ id: id_1 val: val_1 ], map2 = [ id: id_2 val: val_2 ] and map3 = [id: id_1, val: val_3] such that the result map should be merged on the id values:
result_map = [id: id_1 val: {val_1, val_3}, id: id_2 var: {val_2}} ]
The code I've tried:
var a = make(map[string]interface{})
for _, m := range data {
for _, n := range data {
if m["id"] == n["id"] {
for l, k := range n {
c[l] = k
}
}
}
}
Is there a way this can be done? Am using Golang 1.7
Thanks

Simple merge
Yes, they can be merged, but since in the result map there may be multiple values associated to the same key, the value type should be a slice, such as map[string][]string.
To do the merge, simply range over the maps to be merged, and append each value from the source maps to the slice associated with the same key in the result map.
One thing to look out for is that once you do the append, you have to assign back the result slice to the same key in the result map.
This is a simple implementation:
func merge(ms ...map[string]string) map[string][]string {
res := map[string][]string{}
for _, m := range ms {
for k, v := range m {
res[k] = append(res[k], v)
}
}
return res
}
This merge() function has a variadic parameter, which means you may pass any number of maps to it.
Note that you don't need to initialize the slices in the target map, as indexing a map with a key that is not yet in it will result in the zero value of its type (which is nil for slices), and you may append to a nil slice, the builtin append() function takes care of (re-)allocations.
Testing it:
m1 := map[string]string{"id_1": "val_1"}
m2 := map[string]string{"id_2": "val_2"}
m3 := map[string]string{"id_1": "val_3"}
res := merge(m1, m2, m3)
fmt.Println(res)
Output (try it on the Go Playground):
map[id_1:[val_1 val_3] id_2:[val_2]]
Avoiding duplicates
Note that the above merge() will not filter out duplicates, meaning if the same "id_1": "val_1" pair is contained in multiple input maps, it will be listed multiple times in the target like "id_1": ["val_1", "val_1", "val_x"]. To filter out such duplicates (to only list it once in the target), we have to check this before doing the append (and if we've encountered it before, skip the append).
This is how it could be done:
func merge(ms ...map[string]string) map[string][]string {
res := map[string][]string{}
for _, m := range ms {
srcMap:
for k, v := range m {
// Check if (k,v) was added before:
for _, v2 := range res[k] {
if v == v2 {
continue srcMap
}
}
res[k] = append(res[k], v)
}
}
return res
}
Testing it:
m1 := map[string]string{"id_1": "val_1"}
m2 := map[string]string{"id_2": "val_2", "id_1": "val_1"}
m3 := map[string]string{"id_1": "val_3"}
res := merge(m1, m2, m3)
fmt.Println(res)
Output (try it on the Go Playground):
map[id_1:[val_1 val_3] id_2:[val_2]]
We can see that "id_1": "val_1" was included both in m1 and m2, yet the value "val_1" is only listed once in in the slice associated with "id_1" key in the target map.

Related

How to convert a map to a slice of entries?

I'm trying to convert key-value map to slice of pairs, for example given a map like:
m := make(map[int64]int64)
m[521] = 4
m[528] = 8
How do I convert that into a slice of its entries, like: [[521, 4], [528, 8]]
I'm thinking about ranging over all those key-values then create slice for that, but is there any simple code to do that?
package main
import "fmt"
func main() {
//create a map
m := map[int64]int64{512: 8, 513: 9, 234: 9, 392: 0}
//create a slice to hold required values
s := make([][]int64, 0)
//range over map `m` to append to slice `s`
for k, v := range m {
// append each element, with a new slice []int64{k, v}
s = append(s, []int64{k, v})
}
fmt.Println(s)
}
Go 1.18
It is now possible to write a generic function to extract all key-value pairs, i.e. the map entries, with any key and value types.
Notes:
the map iterations are still unordered — using generics doesn't change that.
the constraint for the map key must be comparable
type Pair[K, V any] struct {
First K
Second V
}
func Entries[M ~map[K]V, K comparable, V any](m M) []Pair[K, V] {
entries := make([]Pair[K, V], 0)
for k, v := range m {
entries = append(entries, Pair[K, V]{k, v})
}
return entries
}
The type Pair here is used to preserve type safety in the return value. If you really must return a slice of slices, then it can only be [][]any (or [][2]any) in order to hold different types.
If the map key and value have the same type, of course you can still use Pair but you can also use a type-safe variation of the above:
func Entries[T comparable](m map[T]T) [][2]T {
entries := make([][2]T, 0)
for k, v := range m {
entries = append(entries, [2]T{k, v})
}
return entries
}
Again, T must be comparable or stricter in order to work as a map key.
Playground: https://go.dev/play/p/RwCGmp7MHKW

Difference between `go print(v)` and `go func() { print(v) }()`?

Here is the code:
type field struct {
name string
}
func print(p *field) {
fmt.Println(p.name)
}
func fix1() {
data := []*field{{name: "one"}, {name: "two"}, {name: "three"}}
for _, v := range data {
go print(v)
}
time.Sleep(time.Millisecond * 200)
}
func wrong1() {
data := []*field{{name: "one"}, {name: "two"}, {name: "three"}}
for _, v := range data {
go func() {
print(v)
}()
}
time.Sleep(time.Millisecond * 200)
}
func main() {
wrong1()
}
As far as I understand, all goroutines in function wrong1 share the same local variable v. At the moment of a goroutine execution, the value of v may be equal to any value in data, therefore the function prints random data three times.
However, I am failing to understand why function fix1 behaves differently (it prints each value in data exactly once).
wrong1(): go func() { print(v) }()
Go: Frequently Asked Questions (FAQ)
What happens with closures running as goroutines?
Some confusion may arise when using closures with concurrency.
Consider the following program:
func main() {
done := make(chan bool)
values := []string{"a", "b", "c"}
for _, v := range values {
go func() {
fmt.Println(v)
done <- true
}()
}
// wait for all goroutines to complete before exiting
for _ = range values {
<-done
}
}
One might mistakenly expect to see a, b, c as the output. What you'll
probably see instead is c, c, c. This is because each iteration of the
loop uses the same instance of the variable v, so each closure shares
that single variable. When the closure runs, it prints the value of v
at the time fmt.Println is executed, but v may have been modified
since the goroutine was launched.
To bind the current value of v to each closure as it is launched, one
must modify the inner loop to create a new variable each iteration.
One way is to pass the variable as an argument to the closure:
for _, v := range values {
go func(u string) {
fmt.Println(u)
done <- true
}(v)
}
In this example, the value of v is passed as an argument to the
anonymous function. That value is then accessible inside the function
as the variable u.
Even easier is just to create a new variable, using a declaration
style that may seem odd but works fine in Go:
for _, v := range values {
v := v // create a new 'v'.
go func() {
fmt.Println(v)
done <- true
}()
}
Your wrong1 example,
for _, v := range data {
go func() {
print(v)
}()
}
Playground: https://play.golang.org/p/0w86nvVMt1g
Output:
three
three
three
Your wrong1 example, creating a new variable,
for _, v := range data {
v := v
go func() {
print(v)
}()
}
Playground: https://play.golang.org/p/z5RCI0ZZU8Z
Output:
one
two
three
Your wrong1 example, passing the variable as an argument,
for _, v := range data {
go func(v *field) {
print(v)
}(v)
}
Playground: https://play.golang.org/p/1JVI7XYSqvv
Output:
one
two
three
fix1(): go print(v)
The Go Programming Language Specification
Calls
Given an expression f of function type F,
f(a1, a2, … an)
calls f with arguments a1, a2, … an. Except for one special case,
arguments must be single-valued expressions assignable to the
parameter types of F and are evaluated before the function is called.
Go statements
The function value and parameters are evaluated as usual in the
calling goroutine.
Your fix1 example, evaluating the value of v before the function is called,
for _, v := range data {
go print(v)
}
Playground: https://play.golang.org/p/rN3UNaGi-ge
Output:
one
two
three

Very Confusing variable changes

http://play.golang.org/p/Vd3meom5VF
I have this code for some context free grammar in Go
And I am looking at this code so many times and still don't see any reason for the struct values to be changed. Could anybody see why the change like the following happens?
Rules:
S -> . [DP VP]
VP -> . [V DP]
VP -> . [V DP AdvP]
After I run some functions as in the line
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
Somehow my struct value is changed... I don't know why...
Rules:
S -> . [VP VP]
VP -> . [DP DP]
VP -> . [AdvP AdvP AdvP]
This should have been same as above.
Rules:
S -> DP,VP
VP -> V,DP
VP -> V,DP,AdvP
or2 := []QRS{}
g2 := ToGrammar(cfg2)
fmt.Printf("%s\n", g2)
for _, rule := range g2.Rules {
q := QRS{
one: rule.Src,
two: []string{},
three: rule.Right,
}
or2 = append(or2, OstarCF([]QRS{q}, []string{"sees"}, g2.Nullables(), g2.ChainsTo(g2.Nullables()))...)
}
fmt.Printf("%s\n", g2)
As you see, I do not use any pointer the variable rule, and they are only used to instantiate another struct value, but how come the original struct field rule has changed? The function OstarCF does not do anything about this field rule
func OstarCF(Qs []QRS, R []string, nD map[string]bool, cD map[string][]string) []QRS {
symbols := []string{}
for _, r := range R {
symbols = append(symbols, cD[r]...)
}
product := []QRS{}
for _, Q := range Qs {
a := Q.one
b := Q.two
c := Q.three
if len(c) > 0 && CheckStr(c[0], symbols) {
b = append(b, c[0])
np := QRS{
one: a,
two: b,
three: c[1:],
}
product = append(product, np)
for len(np.three) > 0 && nD[np.three[0]] == true {
np.two = append(np.two, np.three[0])
np = QRS{
one: np.one,
two: np.two,
three: np.three[1:],
}
product = append(product, np)
}
}
}
return product
}
The original Rules field changes because pointers and slices (which are references as well) are used.
Before calling OstarCF, the ChainsTo method is called. It uses the grammar object by value, so a copy is done, but the Rules field is a slice of pointers on Rules. So when this field is copied, it still points to the data of the original object.
Then, in method ChainsTo, there is a loop on the Rules field. It copies the Right field which is a slice of strings (so it still points to data of the original object):
rhs := rule.Right
Finally, a ns variable is declared by slicing rhs:
ns := rhs[:i]
ns = append(ns, rhs[i+1:]...)
At this stage, the ns variable still points to the buffer containing the slice of strings of the original object. Initially, i=0, so ns is an empty slice reusing the buffer. When items are appended, they replace the original data.
That's why your data are changed.
You can fix this problem by explicitly making a copy, for instance by replacing the above lines by:
ns := make( []string, 0, len(rhs) )
ns = append( ns, rhs[:i]...)
ns = append( ns, rhs[i+1:]...)
Go slices have replaced C pointer arithmetic, but they can be almost as dangerous/misleading in some cases.

In Go how to get a slice of values from a map?

If I have a map m is there a better way of getting a slice of the values v than this?
package main
import (
"fmt"
)
func main() {
m := make(map[int]string)
m[1] = "a"
m[2] = "b"
m[3] = "c"
m[4] = "d"
// Can this be done better?
v := make([]string, len(m), len(m))
idx := 0
for _, value := range m {
v[idx] = value
idx++
}
fmt.Println(v)
}
Is there a built-in feature of a map? Is there a function in a Go package, or is this the only way to do this?
As an addition to jimt's post:
You may also use append rather than explicitly assigning the values to their indices:
m := make(map[int]string)
m[1] = "a"
m[2] = "b"
m[3] = "c"
m[4] = "d"
v := make([]string, 0, len(m))
for _, value := range m {
v = append(v, value)
}
Note that the length is zero (no elements present yet) but the capacity (allocated space) is initialized with the number of elements of m. This is done so append does not need to allocate memory each time the capacity of the slice v runs out.
You could also make the slice without the capacity value and let append allocate the memory for itself.
Unfortunately, no. There is no builtin way to do this.
As a side note, you can omit the capacity argument in your slice creation:
v := make([]string, len(m))
The capacity is implied to be the same as the length here.
Go 1.18
You can use maps.Values from the golang.org/x/exp package.
Values returns the values of the map m. The values will be in an indeterminate order.
func main() {
m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d"}
v := maps.Values(m)
fmt.Println(v)
}
The package exp includes experimental code. The signatures may or may not change in the future, and may or may not be promoted to the standard library.
If you don't want to depend on an experimental package, you can easily implement it yourself. In fact, this code is a copy-paste from the exp package:
func Values[M ~map[K]V, K comparable, V any](m M) []V {
r := make([]V, 0, len(m))
for _, v := range m {
r = append(r, v)
}
return r
}
Not necessarily better, but the cleaner way to do this is by defining both the Slice LENGTH and CAPACITY like txs := make([]Tx, 0, len(txMap))
// Defines the Slice capacity to match the Map elements count
txs := make([]Tx, 0, len(txMap))
for _, tx := range txMap {
txs = append(txs, tx)
}
Full example:
package main
import (
"github.com/davecgh/go-spew/spew"
)
type Tx struct {
from string
to string
value uint64
}
func main() {
// Extra touch pre-defining the Map length to avoid reallocation
txMap := make(map[string]Tx, 3)
txMap["tx1"] = Tx{"andrej", "babayaga", 10}
txMap["tx2"] = Tx{"andrej", "babayaga", 20}
txMap["tx3"] = Tx{"andrej", "babayaga", 30}
txSlice := getTXsAsSlice(txMap)
spew.Dump(txSlice)
}
func getTXsAsSlice(txMap map[string]Tx) []Tx {
// Defines the Slice capacity to match the Map elements count
txs := make([]Tx, 0, len(txMap))
for _, tx := range txMap {
txs = append(txs, tx)
}
return txs
}
Simple solution but a lot of gotchas. Read this blog post for more details: https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas
As far as I'm currently aware, go doesn't have a way method for concatenation of strings/bytes in to a resulting string without making at least /two/ copies.
You currently have to grow a []byte since all string values are const, THEN you have to use the string builtin to have the language create a 'blessed' string object, which it will copy the buffer for since something somewhere could have a reference to the address backing the []byte.
If a []byte is suitable then you can gain a very slight lead over the bytes.Join function by making one allocation and doing the copy calls your self.
package main
import (
"fmt"
)
func main() {
m := make(map[int]string)
m[1] = "a" ; m[2] = "b" ; m[3] = "c" ; m[4] = "d"
ip := 0
/* If the elements of m are not all of fixed length you must use a method like this;
* in that case also consider:
* bytes.Join() and/or
* strings.Join()
* They are likely preferable for maintainability over small performance change.
for _, v := range m {
ip += len(v)
}
*/
ip = len(m) * 1 // length of elements in m
r := make([]byte, ip, ip)
ip = 0
for _, v := range m {
ip += copy(r[ip:], v)
}
// r (return value) is currently a []byte, it mostly differs from 'string'
// in that it can be grown and has a different default fmt method.
fmt.Printf("%s\n", r)
}
As of 1.18, this is the best way:
https://stackoverflow.com/a/71635953/130427
Pre 1.18
You can use this maps package:
go get https://github.com/drgrib/maps
Then all you have to call is
values := maps.GetValuesIntString(m)
It's type-safe for that common map combination. You can generate other type-safe functions for any other type of map using the mapper tool in the same package.
Full disclosure: I am the creator of this package. I created it because I found myself rewriting these functions for map repeatedly.

golang map prints out of order

Why is the map printing out of order, and how do I get it in to order?
package main
import (
"fmt"
)
type monthsType struct {
no int
text string
}
var months = map[int]string{
1:"January", 2:"Fabruary", 3:"March", 4:"April", 5:"May", 6:"June",
7:"July", 8:"August", 9:"September", 10:"October", 11:"Novenber", 12:"December",
}
func main(){
for no, month := range months {
fmt.Print(no)
fmt.Println("-" + month)
}
}
Prints out:
10-October
7-July
1-January
9-September
4-April
5-May
2-Fabruary
12-December
11-Novenber
6-June
8-August
3-March
Code:
func DemoSortMap() (int, error) {
fmt.Println("use an array to access items by number:")
am := [2]string{"jan", "feb"}
for i, n := range am {
fmt.Printf("%2d: %s\n", i, n)
}
fmt.Println("maps are non-sorted:")
mm := map[int]string{2: "feb", 1: "jan"}
for i, n := range mm {
fmt.Printf("%2d: %s\n", i, n)
}
fmt.Println("access items via sorted list of keys::")
si := make([]int, 0, len(mm))
for i := range mm {
si = append(si, i)
}
sort.Ints(si)
for _, i := range si {
fmt.Printf("%2d: %s\n", i, mm[i])
}
return 0, nil
}
(most of it stolen from M. Summerfield's book)
output:
use an array to access items by number:
0: jan
1: feb
maps are non-sorted:
2: feb
1: jan
access items via sorted list of keys::
1: jan
2: feb
Maps are not sorted so you may use a slice to sort your map. Mark Summerfield's book "Programming in Go" explains this on page 204 and is highly recommended.
This is very late answer, but from what I have read maps are unsorted by design, and are random as one should not rely on the order.
Besides using the sort package together with a second map, one can also use the fmt.Prinln(theMap), which will print the map sorted.
fmt: print maps in key-sorted order
This will print the map typically as follows:
map[key:value
key:value
key:value
]
But this might not be what you want...
By using the fmt.Sprint one can then manipulate the string if needed.
i.e.
s := fmt.Sprint(theMap)
s1 := s[4 : len(s)-1] // remove map[ and ]
fmt.Println(s1)

Resources