168 votes

Dans Go, comment obtenir une tranche de valeurs à partir d'une carte ?

Si j'ai une carte m Y a-t-il un meilleur moyen d'obtenir une tranche de valeurs ? v que ça ?

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

Existe-t-il une fonction intégrée d'un map ? Existe-t-il une fonction dans un paquetage Go, ou est-ce le seul moyen de le faire ?

95voto

nemo Points 13983

En complément du message de Jimt :

Vous pouvez également utiliser append plutôt que d'assigner explicitement les valeurs à leurs 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)
}

Notez que la longueur est nulle (aucun élément présent pour l'instant) mais la capacité (espace alloué) est initialisée avec le nombre d'éléments de m . Ceci est fait de manière à append n'a pas besoin d'allouer de la mémoire à chaque fois que la capacité de la tranche v s'épuise.

Vous pouvez également make la tranche sans la valeur de la capacité et laisser append allouer la mémoire pour lui-même.

81voto

jimt Points 7028

Malheureusement, non. Il n'y a pas de moyen intégré de le faire.

À titre d'information, vous pouvez omettre l'argument de la capacité dans la création de votre tranche :

v := make([]string, len(m))

La capacité est implicitement la même que la longueur ici.

76voto

TXV Points 215

Go 1.18

Vous pouvez utiliser maps.Values de la golang.org/x/exp paquet.

Values renvoie les valeurs de la carte m. Les valeurs seront dans un ordre indéterminé.

func main() {
    m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d"}
    v := maps.Values(m)
    fmt.Println(v) 
}

Le paquet exp inclut le code expérimental. Les signatures peuvent ou non changer dans le futur, et peuvent ou non être promues à la bibliothèque standard.

Si vous ne voulez pas dépendre d'un paquetage expérimental, vous pouvez implémenter une fonction générique vous-même - ou copier-coller le code de l'application exp paquet :

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
}

12voto

EnchanterIO Points 2135

Ce n'est pas nécessairement mieux, mais la façon la plus propre de procéder consiste à définir à la fois les éléments suivants Longueur et capacité des tranches comme 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)
    }

Exemple complet :

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
}

Une solution simple mais beaucoup d'inconvénients. Lisez cet article de blog pour plus de détails : https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas

1voto

Pour autant que je sache, go n'a pas de méthode permettant de concaténer des chaînes/octets en une chaîne résultante sans faire au moins /deux/ copies.

Actuellement, vous devez faire croître un []byte puisque toutes les valeurs de chaîne sont constantes, puis vous devez utiliser le buildin string pour que le langage crée un objet chaîne 'béni', pour lequel il copiera le tampon puisque quelque part quelque chose pourrait avoir une référence à l'adresse de l'octet []byte.

Si un []octet convient, vous pouvez prendre une très légère avance sur la fonction bytes.Join en effectuant une allocation et en effectuant vous-même les appels de copie.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X