গোলাংয়ে মানচিত্র থেকে মানগুলির টুকরো পাওয়ার কোনও দুর্দান্ত উপায় আছে?


90

আমার কাছে যদি একটি মানচিত্র থাকে তবে মানগুলির একটি টুকরো পাওয়ার আরও ভাল উপায় যদি ভি হয়

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 feature of a map? Is there a function in a Go package, or is this the best code to do if I have to?


1
instead of '_' in your for loop, call it idx and ditch the idx++ business
Peter Agnew

No, he can not, when you range over a map it returns key, value not index, value. In his example he uses 1 as the first key and that will make the indexes in the slice v incorrect because the start index will be 1 not zero, and when it gets to 4 it will be out of range. play.golang.org/p/X8_SbgxK4VX
Popmedic

@Popmedic Actually, yes he can. just replace _ with idx and use idx-1 when assigning the slice values.
hewiefreeman

3
@newplayer66, that is a very dangerous pattern.
Popmedic

উত্তর:



58

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.


I was wondering if this would be any slower (assuming up front allocation)? I did a crude benchmark with a map[int]int and it seemed about 1-2% slower. Any ideas if this is something to worry about or just go with it?
masebase

1
I would assume append to be a little bit slower but that difference is, in most cases, negligible. Benchmark comparing direct assignment and append.
nemo

1
Be careful mixing this with the answer above - appending to the array after you use make([]appsv1.Deployment, len(d)) will append to a bunch of empty elements that were created when you allocated len(d) empty items.
Anirudh Ramanathan

2

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


The answer is not "wrong". The question uses an index and no append for the slice. If you use append on the slice then yes setting the length up front would be bad. Here is the question as it is play.golang.org/p/nsIlIl24Irn Granted that question is not idiomatic go and I was still learning. A slightly improved version more like what you talking about is play.golang.org/p/4SKxC48wg2b
masebase

1
Hi @masebase, I consider it "wrong" because it states: "Unfortunately, no. There is no builtin way to do this.", but there is "better" solution we are both pointing out now 2 years later - using the append() and defining both the length and the capacity. But I do see your point that calling it "wrong" is not accurate neither. I will change my first sentence to: "Not necessarily better, but the cleaner way to do this is". I have talked to ~10 devs and everyone agreed the append() is a cleaner way to convert a map to a slice without using a helper index. I learned this also on the way of posting
Lukas Lukac

1

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)
}

1

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.

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.