web-dev-qa-db-fra.com

Trier Aller les valeurs de carte par clés

Lors de l'itération dans la carte renvoyée dans le code, renvoyée par la fonction de rubrique, les clés n'apparaissent pas dans l'ordre.

Comment puis-je obtenir les clés en ordre/trier la carte pour que les clés soient en ordre et que les valeurs correspondent?

Voici le code .

67
gramme.ninja

Le Go blog: Go maps in action a une excellente explication.

Lors de l'itération sur une carte avec une boucle de plage, l'ordre des itérations est non spécifié et il n'est pas garanti que ce soit la même chose d'une itération au suivant. Depuis Go 1, le runtime randomise l'ordre d'itération de la carte, sous la forme les programmeurs se sont appuyés sur l'ordre d'itération stable du précédent la mise en oeuvre. Si vous avez besoin d'un ordre d'itération stable, vous devez maintenir une structure de données séparée qui spécifie cet ordre.

Voici ma version modifiée de l'exemple de code: http://play.golang.org/p/dvqcGPYy3-

package main

import (
    "fmt"
    "sort"
)

func main() {
    // To create a map as input
    m := make(map[int]string)
    m[1] = "a"
    m[2] = "c"
    m[0] = "b"

    // To store the keys in slice in sorted order
    var keys []int
    for k := range m {
        keys = append(keys, k)
    }
    sort.Ints(keys)

    // To perform the opertion you want
    for _, k := range keys {
        fmt.Println("Key:", k, "Value:", m[k])
    }
}

Sortie:

Key: 0 Value: b
Key: 1 Value: a
Key: 2 Value: c
112
Mingyu

Selon la spécification Go , l'ordre d'itération sur une carte n'est pas défini et peut varier entre les exécutions du programme. En pratique, non seulement il est indéfini, mais en fait, il est intentionnellement randomisé. Cela est dû au fait qu’il était prévisible et que les développeurs du langage Go ne souhaitaient pas que les gens s’appuient sur un comportement non spécifié;.

Ce que vous devrez faire, alors, consiste à tirer les clés dans une tranche, à les trier, puis à les répartir comme suit:

var m map[keyType]valueType
keys := sliceOfKeys(m) // you'll have to implement this
for _, k := range keys {
    v := m[k]
    // k is the key and v is the value; do your computation here
}
12
joshlf

Si, comme moi, vous souhaitez essentiellement le même code de tri à plusieurs endroits ou si vous souhaitez simplement limiter la complexité du code, vous pouvez supprimer le tri lui-même en une fonction distincte, à laquelle vous transmettez la fonction qui le fait. le travail que vous voulez réellement (qui serait différent sur chaque site d’appel, bien sûr).

Avec une mappe avec le type de clé K et le type de valeur V, représentés par <K> et <V> ci-dessous, la fonction de tri commune pourrait ressembler à ceci: le modèle de code Go (que la version 1 de Go ne prend pas en charge):

/* Go apparently doesn't support/allow 'interface{}' as the value (or
/* key) of a map such that any arbitrary type can be substituted at
/* run time, so several of these nearly-identical functions might be
/* needed for different key/value type combinations. */
func sortedMap<K><T>(m map[<K>]<V>, f func(k <K>, v <V>)) {
    var keys []<K>
    for k, _ := range m {
        keys = append(keys, k)
    }
    sort.Strings(keys)  # or sort.Ints(keys), sort.Sort(...), etc., per <K>
    for _, k := range keys {
        v := m[k]
        f(k, v)
    }
}

Appelez-le ensuite avec la mappe d'entrée et une fonction (en prenant (k <K>, v <V>) comme arguments d'entrée) appelée par dessus les éléments de la mappe dans l'ordre des clés triées.

Ainsi, une version du code dans la réponse publiée par Mingu pourrait ressembler à ceci:

package main

import (
    "fmt"
    "sort"
)

func sortedMapIntString(m map[int]string, f func(k int, v string)) {
    var keys []int
    for k, _ := range m {
        keys = append(keys, k)
    }
    sort.Ints(keys)
    for _, k := range keys {
        f(k, m[k])
    }
}

func main() {
    // Create a map for processing
    m := make(map[int]string)
    m[1] = "a"
    m[2] = "c"
    m[0] = "b"

    sortedMapIntString(m,
        func(k int, v string) { fmt.Println("Key:", k, "Value:", v) })
}

La fonction sortedMapIntString() peut être réutilisée pour tout map[int]string (en supposant que le même ordre de tri soit souhaité), en limitant chaque utilisation à deux lignes de code.

Les inconvénients comprennent:

  • Il est plus difficile de lire pour les personnes qui ne sont pas habituées à utiliser des fonctions de première classe.
  • Cela peut être plus lent (je n'ai pas comparé les performances)

D'autres langues ont différentes solutions:

  • Si l'utilisation de <K> et <V> (pour désigner les types de clé et de valeur) vous semble un peu familière, ce modèle de code n'est pas très différent des modèles C++.
  • Clojure et d'autres langues prennent en charge les cartes triées en tant que types de données fondamentaux.
  • Bien que je ne sache pas comment Go fait de range un type de première classe tel qu’il puisse être remplacé par un ordered-range personnalisé (à la place de range dans le code original), je pense que d’autres langages fournissent des itérateurs assez puissants pour accomplir la même chose.
2

Toutes les réponses ici contiennent maintenant l'ancien comportement des cartes. Dans Go 1.12+, vous pouvez simplement imprimer une valeur de carte qui sera triée par clé automatiquement. Ceci a été ajouté car il permet de tester facilement les valeurs de la carte.

func main() {
    m := map[int]int{3: 5, 2: 4, 1: 3}
    fmt.Println(m)

    // In Go 1.12+
    // Output: map[1:3 2:4 3:5]

    // Before Go 1.12 (the order was undefined)
    // map[3:5 2:4 1:3]
}

Les cartes sont maintenant imprimées dans un ordre de clé pour faciliter les tests. Les règles de commande sont les suivantes:

  • Le cas échéant, rien ne se compare
  • commande ints, floats, and strings by <
  • NaN compare moins que les flotteurs non-NaN
  • bool compare false avant true
  • Complexe compare réel, puis imaginaire
  • Pointeurs comparés par adresse machine
  • Les valeurs de canal comparent par adresse machine
  • Les structures comparent chaque champ
  • Les tableaux comparent chaque élément
  • Les valeurs d'interface comparent d'abord par reflect.Type décrivant le type concret, puis par valeur concrète, comme décrit dans les règles précédentes.

Lors de l'impression de cartes, les valeurs de clé non réflexives telles que NaN étaient précédemment affichées sous la forme <nil>. À partir de cette version, les valeurs correctes sont imprimées.

En savoir plus ici.

0
Inanc Gumus

En réponse à James Craig Burley's answer . Pour créer une conception propre et réutilisable, on peut choisir une approche plus orientée objet. De cette façon, les méthodes peuvent être liées en toute sécurité aux types de la carte spécifiée. Pour moi, cette approche est plus propre et organisée.

Exemple:

package main

import (
    "fmt"
    "sort"
)

type myIntMap map[int]string

func (m myIntMap) sort() (index []int) {
    for k, _ := range m {
        index = append(index, k)
    }
    sort.Ints(index)
    return
}

func main() {
    m := myIntMap{
        1:  "one",
        11: "eleven",
        3:  "three",
    }
    for _, k := range m.sort() {
        fmt.Println(m[k])
    }
}

Exemple de terrain de jeu étendu avec plusieurs types de cartes.

Note importante

Dans tous les cas, la carte et la tranche triée sont découplées à partir du moment où la boucle for sur la carte range est terminée. Cela signifie que si la carte est modifiée après la logique de tri, mais avant de l'utiliser, vous pouvez avoir des problèmes. (Pas de thread/Aller routine en sécurité). En cas de modification de l'accès en écriture sur les cartes parallèles, vous devez utiliser un mutex autour des écritures et de la boucle for triée.

mutex.Lock()
for _, k := range m.sort() {
    fmt.Println(m[k])
}
mutex.Unlock()
0
Tim

Bien que la plupart de ces réponses soient correctes, je trouve souvent que la solution la plus simple consiste à utiliser une boucle forçée en C (bien que si vos clés sont une série de ints):

m := make(map[int]string)
m[1] = "a"
m[2] = "c"
m[0] = "b"

for i := 0; i < len(m); i++ {
    fmt.Println("Key:", i, "Value:", m[i])
}

Sortie:

Key: 0 Value: b
Key: 1 Value: a
Key: 2 Value: c
0
Roshambo