-
-
Save flaviocopes/bed52cc2ac407137b3931bc864b4e31b to your computer and use it in GitHub Desktop.
import "sort" | |
ages := map[string]int{ | |
"a": 1, | |
"c": 3, | |
"d": 4, | |
"b": 2, | |
} | |
names := make([]string, 0, len(ages)) | |
for name := range ages { | |
names = append(names, name) | |
} | |
sort.Strings(names) //sort by key | |
for _, name := range names { | |
//... | |
} |
- You cannot sort a map.
- Iteration of maps is by design random.
So, this code for example:
package main
import(
"fmt"
)
func main() {
myRecords := make(map[string][]string)
myRecords["testKey"] = append(myRecords["testKey"], "a value")
myRecords["testKey"] = append(myRecords["testKey"], "another value")
myRecords["testKey"] = append(myRecords["testKey"], "just another value")
myRecords["newKey"] = append(myRecords["newKey"], "and another value")
myRecords["newKey"] = append(myRecords["newKey"], "and another")
myRecords["lastKey"] = append(myRecords["lastKey"], "and another")
myRecords["lastKey"] = append(myRecords["lastKey"], "and the last one")
myRecords["lastKey"] = append(myRecords["lastKey"], "and the last one")
for k, v := range myRecords {
fmt.Printf("k: %s, v: %v\n", k, v)
}
}
Results to this output (which will also be most probably different on each run):
$ for i in `seq 1 25` ; do go run gomaprange.go | head -1 ; sleep 0.2 ; done
k: testKey, v: [a value another value just another value]
k: newKey, v: [and another value and another]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: newKey, v: [and another value and another]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: newKey, v: [and another value and another]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: lastKey, v: [and another and the last one and the last one]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: newKey, v: [and another value and another]
k: testKey, v: [a value another value just another value]
k: testKey, v: [a value another value just another value]
k: lastKey, v: [and another and the last one and the last one]
k: testKey, v: [a value another value just another value]
So, the only way AFAIK you can "sort" a map, is to keep track of its keys in an array, or a struct which you can then sort, and do loookups on your map by iterating over this array when you need sorted access, like the code below:
package main
import(
"fmt"
"sort"
)
func main() {
myRecords := make(map[int]string)
myRecords[0] = "a new test"
myRecords[1] = "that will show"
myRecords[2] = "a way of"
myRecords[3] = "keeping order"
myRecords[4] = "and avoid"
myRecords[5] = "CHAOS"
fmt.Printf("always random:\n")
for k, v := range myRecords {
fmt.Printf("k: %s, v: %v\n", k, v)
}
//now let's create & sort an array with our map keys
sortedKeys := make([]int, 0, len(myRecords))
for k := range myRecords {
sortedKeys = append(sortedKeys, k)
}
sort.Ints(sortedKeys)
fmt.Printf("always sorted:\n")
for k := range sortedKeys {
fmt.Printf("k: %s, v: %v\n", k, myRecords[k])
}
}
HTH
@zmf
I am wondering what's the reason that the iteration of the maps is by design random.
@vincent6767 because devs started to depend on map key order in their apps. And it was not deterministic, so it was decided to randomize keys order to clearly communicate, that key order is not guaranteed.
We can workaround it, but it would be great to have native ordered map in golang ...
@ernierasta Python dicts (cf GoLang maps) have preserved insertion order of keys for several years now. And somehow managed to improve the performance of dicts at the same time.
Java has LinkedhashMap. C# you have to dig around, but there are OrderedDictionary classes that preserve insertion order when you iterate over dictionary keys.
Is there something similar for Go?
Extracting the keys, sorting them and iterating over the sorted list of keys seems a long-winded and inefficient way to do things for many applications.
To take a simple example, imagine building a map into a CSV row and preserving field order in each row. Concrete example: here's some code to build a record to send to InfluxDB. The keys and values are inserted into the metas map, then the keys have to be sorted as a second step because in Go, map key order is undefined.
influxRow := []string{meta.Measure}
metas := map[string]string{
"run": meta.Run,
"system": meta.System,
"phase": meta.Phase,
"build": meta.Build,
"summary": strconv.FormatBool(meta.Summary),
"step": meta.Step,
"label": meta.Label,
"responseCode": responseCode,
"success": strconv.FormatBool(success),
"user": meta.User,
"xrequestid": meta.XRequestID,
"errorMessage": meta.ErrorMessage,
}
fieldNames := make([]string, 0, len(metas))
for k := range metas {
fieldNames = append(fieldNames, k)
}
sort.Strings(fieldNames)
for _, fieldName := range fieldNames {
value := metas[fieldName]
if value != "" {
influxRow = append(influxRow, influxKvPair(fieldName, value))
}
}
influxRow = append(influxRow, " " + influxKvPair("elapsed", fmt.Sprintf("%d", elapsed.Milliseconds())))
if responseSize > -1 {
influxRow = append(influxRow, influxKvPair("responseSize", fmt.Sprintf("%d", responseSize)))
}
influxRow = append(influxRow, " " + fmt.Sprintf( "%d", timestamp.Local().UnixNano()))
messagesChan <- strings.Join(influxRow, ",")
A better way would be to order the initialiser keys alphabetically and be able to rely on range to iterate over the keys in original insertion order. Then you can remove the O(n log n) sort entirely.
@mellorn-anz I completely agree with you, I was just saying how it is and why it is like that. But while there are workarounds (like: https://github.com/elliotchance/orderedmap) I would also love to see native ordered map in go.
@zmf I am wondering what's the reason that the iteration of the maps is by design random.
I'm guessing it has something to do with garbage collection and the hash backing store scaling up or down. Originally, if I remember correctly, insertion order was mostly predictable, but not always, and because it wasn't guaranteed, the Go devs decided to make sure people wouldn't rely on the insertion order being stable.
I think it was changed again fairly recently to make things like testing easier.
Covert a map into a String Array ?