Podczas iteracji po zwróconej mapie w kodzie, zwróconej przez funkcję topic, klucze nie pojawiają się w kolejności.
Jak mogę sprawić, aby klucze były uporządkowane / posortowane na mapie, aby klucze były w porządku, a wartości odpowiadały?
Oto kod .
Odpowiedzi:
Go blog: Mapy w akcji ma doskonałe wyjaśnienie.
Oto moja zmodyfikowana wersja przykładowego kodu: 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 keys := make([]int, len(m)) i := 0 for k := range m { keys[i] = k i++ } sort.Ints(keys) // To perform the opertion you want for _, k := range keys { fmt.Println("Key:", k, "Value:", m[k]) } }
Wynik:
Key: 0 Value: b Key: 1 Value: a Key: 2 Value: c
źródło
keys := make([]int, len(m))
a następnie wstawić według indeksukeys[i] = k
zamiastappend
Zgodnie ze specyfikacją Go , kolejność iteracji na mapie jest nieokreślona i może różnić się między uruchomieniami programu. W praktyce jest nie tylko niezdefiniowany, ale w rzeczywistości jest celowo losowy. Dzieje się tak, ponieważ kiedyś był przewidywalny, a programiści języka Go nie chcieli, aby ludzie polegali na nieokreślonym zachowaniu, więc celowo losowali to, aby poleganie na tym zachowaniu było niemożliwe.
W takim razie musisz pociągnąć klucze do kawałka, posortować je, a następnie przesunąć po kawałku w następujący sposób:
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 }
źródło
Wszystkie odpowiedzi tutaj zawierają teraz stare zachowanie map. W Go 1.12+ możesz po prostu wydrukować wartość mapy, a zostanie ona automatycznie posortowana według klucza. Zostało to dodane, ponieważ umożliwia łatwe testowanie wartości map.
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] }
Przeczytaj więcej tutaj .
źródło
W odpowiedzi na James Craig Burley za odpowiedź . Aby stworzyć czysty i nadający się do ponownego wykorzystania projekt, można wybrać podejście bardziej zorientowane obiektowo. W ten sposób metody można bezpiecznie powiązać z typami określonej mapy. Wydaje mi się, że takie podejście jest czystsze i zorganizowane.
Przykład:
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]) } }
Przykład rozszerzonego placu zabaw z wieloma typami map.
Ważna uwaga
We wszystkich przypadkach mapa i posortowany wycinek są oddzielane od momentu zakończenia
for
pętli nad mapąrange
. Oznacza to, że jeśli mapa zostanie zmodyfikowana po logice sortowania, ale zanim jej użyjesz, możesz wpaść w kłopoty. (Nie jest bezpieczny dla wątków / przejść). Jeśli nastąpi zmiana równoległego dostępu do zapisu Map, będziesz musiał użyć muteksu wokół zapisów i posortowanejfor
pętli.mutex.Lock() for _, k := range m.sort() { fmt.Println(m[k]) } mutex.Unlock()
źródło
Jeśli, tak jak ja, okaże się, że potrzebujesz zasadniczo tego samego kodu sortowania w więcej niż jednym miejscu lub po prostu chcesz zachować złożoność kodu, możesz przenieść samo sortowanie do oddzielnej funkcji, do której przekazujesz funkcję, która to robi rzeczywistą pracę, jaką chcesz (która będzie oczywiście inna w każdym miejscu telefonicznym).
Biorąc pod uwagę, mapa z kluczem typu
K
i rodzaju wartościV
, reprezentowane<K>
i<V>
poniżej, wspólna funkcja sortowania może wyglądać ten szablon Go-kodu (który Go w wersji 1 nie obsługuje jak jest):/* 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) } }
Następnie wywołaj go z mapą wejściową i funkcją (przyjmującą
(k <K>, v <V>)
jako argumenty wejściowe), która jest wywoływana nad elementami mapy w kolejności posortowanych kluczy.Tak więc wersja kodu w odpowiedzi opublikowanej przez Mingu może wyglądać następująco:
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) }) }
sortedMapIntString()
Funkcja może być ponownie wykorzystane dla każdegomap[int]string
(zakładając, że pożądany jest taki sam porządek), utrzymując każdym użyciu tylko dwóch przewodów kodu.Wady obejmują:
Inne języki mają różne rozwiązania:
<K>
and<V>
(do oznaczania typów dla klucza i wartości) wygląda trochę znajomo, ten szablon kodu nie różni się zbytnio od szablonów C ++.range
typ pierwszej klasy, tak aby można go było zastąpić niestandardowymordered-range
(zamiastrange
w oryginalnym kodzie), myślę, że niektóre inne języki zapewniają iteratory, które są wystarczająco potężne, aby osiągnąć to samo rzecz.źródło
var m map[interface{}]interface{}
jest całkowicie legalne. Plac zabawGo 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
. Jeśli (inaczej niż za pomocąunsafe
lub podobnego) możesz pokazać, jak dowolny typ można zastąpić w czasie wykonywania, zapewniając w ten sposób jedną ogólną procedurę sortowania, byłoby świetnie! (Nie mogłem tego rozgryźć, miesiące temu.)To zapewnia przykład kodu na mapie sortowania. Zasadniczo to, co zapewniają:
var keys []int for k := range myMap { keys = append(keys, k) } sort.Ints(keys) // Benchmark1-8 2863149 374 ns/op 152 B/op 5 allocs/op
i oto, co sugerowałbym użycie zamiast tego :
keys := make([]int, 0, len(myMap)) for k := range myMap { keys = append(keys, k) } sort.Ints(keys) // Benchmark2-8 5320446 230 ns/op 80 B/op 2 allocs/op
Pełny kod można znaleźć w tym Go Playground .
źródło