In golang is there a nice way of getting a slice of values from a map?

前端 未结 5 1923
小蘑菇
小蘑菇 2020-12-23 15:38

If I have a map m is there a better way of getting a slice of the values v then

package main
import (
  \"fmt\"
)

func main() {
    m := make(map[int]string         


        
相关标签:
5条回答
  • 2020-12-23 16:10

    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.

    0 讨论(0)
  • 2020-12-23 16:13

    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

    0 讨论(0)
  • 2020-12-23 16:16

    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.

    0 讨论(0)
  • 2020-12-23 16:23

    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)
    }
    
    0 讨论(0)
  • 2020-12-23 16:35

    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.

    0 讨论(0)
提交回复
热议问题