How to create map keys in a loop? - go

I do according to the textbook, but there is an error
panic: assignment to entry in nil map (in line keyval["{x[i]}"] = 0)
package main
import "fmt"
func main() {
x := [3]string{"aa","bb","cc"}
var keyval map[string]int
for i := 0; i < len(x); i++ {
keyval["{x[i]}"] = 0
}
fmt.Println(keyval)
}
I tried to use keyval["x[i]"] = 0 but the effect is the same

You must first initialize your map:
keyval := make(map[string]int)
According to this blog post:
Map types are reference types, like pointers or slices, and so the
value of m above is nil; it doesn't point to an initialized map.
GoPlay here:
https://play.golang.org/p/2JuPS1J7KK
Edit to answer OP's followup. If you're looking to use the strings from your slice as the key to the map, you need to make an additional change:
keyval[x[i]] = 0
GoPlay here:
https://play.golang.org/p/feMSwvbEGS

Related

Remove all map key in Golang [duplicate]

I'm looking for something like the c++ function .clear() for the primitive type map.
Or should I just create a new map instead?
Update: Thank you for your answers. By looking at the answers I just realized that sometimes creating a new map may lead to some inconsistency that we don't want. Consider the following example:
var a map[string]string
var b map[string]string
func main() {
a = make(map[string]string)
b=a
a["hello"]="world"
a = nil
fmt.Println(b["hello"])
}
I mean, this is still different from the .clear() function in c++, which will clear the content in the object.
You should probably just create a new map. There's no real reason to bother trying to clear an existing one, unless the same map is being referred to by multiple pieces of code and one piece explicitly needs to clear out the values such that this change is visible to the other pieces of code.
So yeah, you should probably just say
mymap = make(map[keytype]valtype)
If you do really need to clear the existing map for whatever reason, this is simple enough:
for k := range m {
delete(m, k)
}
Unlike C++, Go is a garbage collected language. You need to think things a bit differently.
When you make a new map
a := map[string]string{"hello": "world"}
a = make(map[string]string)
the original map will be garbage-collected eventually; you don't need to clear it manually. But remember that maps (and slices) are reference types; you create them with make(). The underlying map will be garbage-collected only when there are no references to it.
Thus, when you do
a := map[string]string{"hello": "world"}
b := a
a = make(map[string]string)
the original array will not be garbage collected (until b is garbage-collected or b refers to something else).
// Method - I , say book is name of map
for k := range book {
delete(book, k)
}
// Method - II
book = make(map[string]int)
// Method - III
book = map[string]int{}
Go 1.18 and above
You can use maps.Clear. The function belongs to the package golang.org/x/exp/maps (experimental and not covered by the compatibility guarantee)
Clear removes all entries from m, leaving it empty.
Example usage:
func main() {
testMap := map[string]int{"gopher": 1, "badger": 2}
maps.Clear(testMap)
fmt.Println(testMap)
testMap["zebra"] = 2000
fmt.Println(testMap)
}
Playground: https://go.dev/play/p/qIdnGrd0CYs?v=gotip
If you don't want to depend on experimental packages, you can copy-paste the source, which is actually extremely simple:
func Clear[M ~map[K]V, K comparable, V any](m M) {
for k := range m {
delete(m, k)
}
}
IMPORTANT NOTE: just as with the builtin delete — which the implementation of maps.Clear uses —, this does not remove irreflexive keys from the map. The reason is that for irreflexive keys, by definition, x == x is false. Irreflexive keys are NaN floats and every other type that supports comparison operators but contains NaN floats somewhere.
See this code to understand what this entails:
func main() {
m := map[float64]string{}
m[1.0] = "foo"
k := math.NaN()
fmt.Println(k == k) // false
m[k] = "bar"
maps.Clear(m)
fmt.Printf("len: %d, content: %v\n", len(m), m)
// len: 1, content: map[NaN:bar]
a := map[[2]float64]string{}
a[[2]float64{1.0, 2.0}] = "foo"
h := [2]float64{1.0, math.NaN()}
fmt.Println(h == h) // false
a[h] = "bar"
maps.Clear(a)
fmt.Printf("len: %d, content: %v\n", len(a), a)
// len: 1, content: map[[1 NaN]:bar]
}
Playground: https://go.dev/play/p/LWfiD3iPA8Q
A clear builtin is being currently discussed (Autumn 2022) that, if added to next Go releases, will delete also irreflexive keys.
For the method of clearing a map in Go
for k := range m {
delete(m, k)
}
It only works if m contains no key values containing NaN.
delete(m, k) doesn't work for any irreflexive key (such as math.NaN()), but also structs or other comparable types with any NaN float in it. Given struct{ val float64 } with NaN val is also irreflexive (Quote by blackgreen comment)
To resolve this issue and support clearing a map in Go, one buildin clear(x) function could be available in the new release, for more details, please refer to add clear(x) builtin, to clear map, zero content of slice, ptr-to-array
If you are trying to do this in a loop, you can take advantage of the initialization to clear out the map for you. For example:
for i:=0; i<2; i++ {
animalNames := make(map[string]string)
switch i {
case 0:
animalNames["cat"] = "Patches"
case 1:
animalNames["dog"] = "Spot";
}
fmt.Println("For map instance", i)
for key, value := range animalNames {
fmt.Println(key, value)
}
fmt.Println("-----------\n")
}
When you execute this, it clears out the previous map and starts with an empty map. This is verified by the output:
$ go run maptests.go
For map instance 0
cat Patches
-----------
For map instance 1
dog Spot
-----------

"..." notation in append() doesn't work for appending different type slices [duplicate]

This question already has answers here:
Type converting slices of interfaces
(9 answers)
Closed 4 months ago.
I need an abstracted slice that contains multiple types. The most simplified code is this:
package main
import "fmt"
type A interface{}
type X string
func main() {
sliceA := make([]A, 0, 0)
sliceX := []X{"x1", "x2"}
var appendedSlice []A
appendedSlice = append(sliceA, sliceX[0], sliceX[1]) // (1) works
appendedSlice = append(sliceA, sliceX...) // (2) doesn't work
fmt.Println(appendedSlice)
}
In my real program, the interface A defines some functions, and X and also other types implement it.
Line (2) raises an error cannot use sliceX (type []X) as type []A in append.
I thought (2) is a syntax sugar for (1), but I'm probably missing something... Do I have to always add an element X into slice A one by one?
Thank you guys in advance!
The problem is that interface{} and string are two different types.
To convert a slice from string to interface{} you will have to do it in one of the following ways:
create sliceA and initialize its size to sliceX length
sliceA := make([]A, len(sliceX))
for ix, item := range sliceX {
sliceA[ix] = item
}
dynamically append sliceX items to appendedSlice
var appendedSlice []A
for ix := range sliceX {
appendedSlice = append(appendedSlice, sliceX[ix])
}
Please read more here
Convert []string to []interface{}

Do map of pointers is different with common way of using maps

I want to create cache with map. As map doesn't allow reference to its value, so it's not possible to change values in called functions.
After some search, I found, it's possible with creating map of pointer (of struct). It Almost solve problem and can work like variable by reference
But as i found a few using of this method for map. I worry about using it to be safe.
Is anyone has experience of using map of pointer? and is it right way to use it?
package main
import "fmt"
type Cache struct {
name string
counter int
}
func incr(c Cache) {
c.counter += 1
}
func incrp(c *Cache) {
c.counter += 2
}
func main() {
m := make(map[string]Cache)
m["james"] = Cache{name: "James", counter: 10}
c := m["james"]
incr(c)
fmt.Println(c.name, c.counter) // James 10
mp := make(map[string]*Cache)
mp["james"] = &Cache{name: "James", counter: 10}
cp := mp["james"]
incrp(cp)
fmt.Println(cp.name, cp.counter) // James 12
}
edited: My text had some confusing words and sentences, that caused to misunderstanding, so i tried to fixed it
You can accomplish this and still have a map of non-pointers, with a pointer receiver on the struct:
package main
import "fmt"
type Cache struct {
name string
counter int
}
func (c *Cache) incr() { // the '(c *Cache)' is the receiver;
c.counter += 1 // it makes incr() a method, not just a function
}
func main() {
m := make(map[string]Cache)
m["james"] = Cache{name: "James", counter: 10}
c := m["james"]
c.incr()
fmt.Println(c.name, c.counter)
}
Output:
James 11
If receivers and methods are new to you, here is where they are mentioned in the Tour of Go: https://tour.golang.org/methods/1
Note the page about pointer receivers a few steps later in the Tour: https://tour.golang.org/methods/4

Interface conversion in slice of slice

I wrote this example code (https://play.golang.org/p/u_oz5X4aU07):
func main() {
var obj interface{}
json.Unmarshal([]byte("[[1,2],[3,4]]"), &obj)
val := obj.([][]int)
fmt.Println(val)
}
Why I get the error:
interface conversion: interface {} is []interface {}, not [][]int
Is there a simple way to transform obj in a slice of slice?
This code works, but I'd like something more compact and efficient.
var val [][]float64
for r, v := range obj.([]interface{}) {
val = append(val,nil)
for _, w := range v.([]interface{}) {
val[r] = append(val[r], w.(float64))
}
}
No, ultimately you'll have to loop over the two slices!
You can read here why you can't just use one as the other:
https://research.swtch.com/interfaces
This answer might also be useful:
Why golang struct array cannot be assigned to an interface array
Essentially it's because the interface is stored as a 2 word pair, one defining the type and one the values.
You have to manually convert to the required type by visiting all the values in for-range loops.

initializing a struct containing a slice of structs in golang

I have a struct that I want to initialize with a slice of structs in golang, but I'm trying to figure out if there is a more efficient version of appending every newly generated struct to the slice:
package main
import (
"fmt"
"math/rand"
)
type LuckyNumber struct {
number int
}
type Person struct {
lucky_numbers []LuckyNumber
}
func main() {
count_of_lucky_nums := 10
// START OF SECTION I WANT TO OPTIMIZE
var tmp []LuckyNumber
for i := 0; i < count_of_lucky_nums; i++ {
tmp = append(tmp, LuckyNumber{rand.Intn(100)})
}
a := Person{tmp}
// END OF SECTION I WANT TO OPTIMIZE
fmt.Println(a)
}
You can use make() to allocate the slice in "full-size", and then use a for range to iterate over it and fill the numbers:
tmp := make([]LuckyNumber, 10)
for i := range tmp {
tmp[i].number = rand.Intn(100)
}
a := Person{tmp}
fmt.Println(a)
Try it on the Go Playground.
Note that inside the for I did not create new "instances" of the LuckyNumber struct, because the slice already contains them; because the slice is not a slice of pointers. So inside the for loop all we need to do is just use the struct value designated by the index expression tmp[i].
You can use make() the way icza proposes, you can also use it this way:
tmp := make([]LuckyNumber, 0, countOfLuckyNums)
for i := 0; i < countOfLuckyNums; i++ {
tmp = append(tmp, LuckyNumber{rand.Intn(100)})
}
a := Person{tmp}
fmt.Println(a)
This way, you don't have to allocate memory for tmp several times: you just do it once, when calling make. But, contrary to the version where you would call make([]LuckyNumber, countOfLuckyNums), here, tmp only contains initialized values, not uninitialized, zeroed values. Depending on your code, it might make a difference or not.

Resources