-
-
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 { | |
//... | |
} |
@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.
@zmf
I am wondering what's the reason that the iteration of the maps is by design random.