golang map prints out of order - dictionary

Why is the map printing out of order, and how do I get it in to order?
package main
import (
"fmt"
)
type monthsType struct {
no int
text string
}
var months = map[int]string{
1:"January", 2:"Fabruary", 3:"March", 4:"April", 5:"May", 6:"June",
7:"July", 8:"August", 9:"September", 10:"October", 11:"Novenber", 12:"December",
}
func main(){
for no, month := range months {
fmt.Print(no)
fmt.Println("-" + month)
}
}
Prints out:
10-October
7-July
1-January
9-September
4-April
5-May
2-Fabruary
12-December
11-Novenber
6-June
8-August
3-March

Code:
func DemoSortMap() (int, error) {
fmt.Println("use an array to access items by number:")
am := [2]string{"jan", "feb"}
for i, n := range am {
fmt.Printf("%2d: %s\n", i, n)
}
fmt.Println("maps are non-sorted:")
mm := map[int]string{2: "feb", 1: "jan"}
for i, n := range mm {
fmt.Printf("%2d: %s\n", i, n)
}
fmt.Println("access items via sorted list of keys::")
si := make([]int, 0, len(mm))
for i := range mm {
si = append(si, i)
}
sort.Ints(si)
for _, i := range si {
fmt.Printf("%2d: %s\n", i, mm[i])
}
return 0, nil
}
(most of it stolen from M. Summerfield's book)
output:
use an array to access items by number:
0: jan
1: feb
maps are non-sorted:
2: feb
1: jan
access items via sorted list of keys::
1: jan
2: feb

Maps are not sorted so you may use a slice to sort your map. Mark Summerfield's book "Programming in Go" explains this on page 204 and is highly recommended.

This is very late answer, but from what I have read maps are unsorted by design, and are random as one should not rely on the order.
Besides using the sort package together with a second map, one can also use the fmt.Prinln(theMap), which will print the map sorted.
fmt: print maps in key-sorted order
This will print the map typically as follows:
map[key:value
key:value
key:value
]
But this might not be what you want...
By using the fmt.Sprint one can then manipulate the string if needed.
i.e.
s := fmt.Sprint(theMap)
s1 := s[4 : len(s)-1] // remove map[ and ]
fmt.Println(s1)

Related

How to convert a map to a slice of entries?

I'm trying to convert key-value map to slice of pairs, for example given a map like:
m := make(map[int64]int64)
m[521] = 4
m[528] = 8
How do I convert that into a slice of its entries, like: [[521, 4], [528, 8]]
I'm thinking about ranging over all those key-values then create slice for that, but is there any simple code to do that?
package main
import "fmt"
func main() {
//create a map
m := map[int64]int64{512: 8, 513: 9, 234: 9, 392: 0}
//create a slice to hold required values
s := make([][]int64, 0)
//range over map `m` to append to slice `s`
for k, v := range m {
// append each element, with a new slice []int64{k, v}
s = append(s, []int64{k, v})
}
fmt.Println(s)
}
Go 1.18
It is now possible to write a generic function to extract all key-value pairs, i.e. the map entries, with any key and value types.
Notes:
the map iterations are still unordered — using generics doesn't change that.
the constraint for the map key must be comparable
type Pair[K, V any] struct {
First K
Second V
}
func Entries[M ~map[K]V, K comparable, V any](m M) []Pair[K, V] {
entries := make([]Pair[K, V], 0)
for k, v := range m {
entries = append(entries, Pair[K, V]{k, v})
}
return entries
}
The type Pair here is used to preserve type safety in the return value. If you really must return a slice of slices, then it can only be [][]any (or [][2]any) in order to hold different types.
If the map key and value have the same type, of course you can still use Pair but you can also use a type-safe variation of the above:
func Entries[T comparable](m map[T]T) [][2]T {
entries := make([][2]T, 0)
for k, v := range m {
entries = append(entries, [2]T{k, v})
}
return entries
}
Again, T must be comparable or stricter in order to work as a map key.
Playground: https://go.dev/play/p/RwCGmp7MHKW

Merge Maps in Golang

I need to merge multiple maps map1 = [ id: id_1 val: val_1 ], map2 = [ id: id_2 val: val_2 ] and map3 = [id: id_1, val: val_3] such that the result map should be merged on the id values:
result_map = [id: id_1 val: {val_1, val_3}, id: id_2 var: {val_2}} ]
The code I've tried:
var a = make(map[string]interface{})
for _, m := range data {
for _, n := range data {
if m["id"] == n["id"] {
for l, k := range n {
c[l] = k
}
}
}
}
Is there a way this can be done? Am using Golang 1.7
Thanks
Simple merge
Yes, they can be merged, but since in the result map there may be multiple values associated to the same key, the value type should be a slice, such as map[string][]string.
To do the merge, simply range over the maps to be merged, and append each value from the source maps to the slice associated with the same key in the result map.
One thing to look out for is that once you do the append, you have to assign back the result slice to the same key in the result map.
This is a simple implementation:
func merge(ms ...map[string]string) map[string][]string {
res := map[string][]string{}
for _, m := range ms {
for k, v := range m {
res[k] = append(res[k], v)
}
}
return res
}
This merge() function has a variadic parameter, which means you may pass any number of maps to it.
Note that you don't need to initialize the slices in the target map, as indexing a map with a key that is not yet in it will result in the zero value of its type (which is nil for slices), and you may append to a nil slice, the builtin append() function takes care of (re-)allocations.
Testing it:
m1 := map[string]string{"id_1": "val_1"}
m2 := map[string]string{"id_2": "val_2"}
m3 := map[string]string{"id_1": "val_3"}
res := merge(m1, m2, m3)
fmt.Println(res)
Output (try it on the Go Playground):
map[id_1:[val_1 val_3] id_2:[val_2]]
Avoiding duplicates
Note that the above merge() will not filter out duplicates, meaning if the same "id_1": "val_1" pair is contained in multiple input maps, it will be listed multiple times in the target like "id_1": ["val_1", "val_1", "val_x"]. To filter out such duplicates (to only list it once in the target), we have to check this before doing the append (and if we've encountered it before, skip the append).
This is how it could be done:
func merge(ms ...map[string]string) map[string][]string {
res := map[string][]string{}
for _, m := range ms {
srcMap:
for k, v := range m {
// Check if (k,v) was added before:
for _, v2 := range res[k] {
if v == v2 {
continue srcMap
}
}
res[k] = append(res[k], v)
}
}
return res
}
Testing it:
m1 := map[string]string{"id_1": "val_1"}
m2 := map[string]string{"id_2": "val_2", "id_1": "val_1"}
m3 := map[string]string{"id_1": "val_3"}
res := merge(m1, m2, m3)
fmt.Println(res)
Output (try it on the Go Playground):
map[id_1:[val_1 val_3] id_2:[val_2]]
We can see that "id_1": "val_1" was included both in m1 and m2, yet the value "val_1" is only listed once in in the slice associated with "id_1" key in the target map.

Getting a slice of keys from a map

Is there any simpler/nicer way of getting a slice of keys from a map in Go?
Currently I am iterating over the map and copying the keys to a slice:
i := 0
keys := make([]int, len(mymap))
for k := range mymap {
keys[i] = k
i++
}
This is an old question, but here's my two cents. PeterSO's answer is slightly more concise, but slightly less efficient. You already know how big it's going to be so you don't even need to use append:
keys := make([]int, len(mymap))
i := 0
for k := range mymap {
keys[i] = k
i++
}
In most situations it probably won't make much of a difference, but it's not much more work, and in my tests (using a map with 1,000,000 random int64 keys and then generating the array of keys ten times with each method), it was about 20% faster to assign members of the array directly than to use append.
Although setting the capacity eliminates reallocations, append still has to do extra work to check if you've reached capacity on each append.
For example,
package main
func main() {
mymap := make(map[int]string)
keys := make([]int, 0, len(mymap))
for k := range mymap {
keys = append(keys, k)
}
}
To be efficient in Go, it's important to minimize memory allocations.
You also can take an array of keys with type []Value by method MapKeys of struct Value from package "reflect":
package main
import (
"fmt"
"reflect"
)
func main() {
abc := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
keys := reflect.ValueOf(abc).MapKeys()
fmt.Println(keys) // [a b c]
}
Go now has generics. You can get the keys of any map with maps.Keys.
Example usage:
intMap := map[int]int{1: 1, 2: 2}
intKeys := maps.Keys(intMap)
// intKeys is []int
fmt.Println(intKeys)
strMap := map[string]int{"alpha": 1, "bravo": 2}
strKeys := maps.Keys(strMap)
// strKeys is []string
fmt.Println(strKeys)
maps package is found in golang.org/x/exp/maps. This is experimental and outside of Go compatibility guarantee. They aim to move it into the std lib in Go 1.19 the future.
Playground: https://go.dev/play/p/fkm9PrJYTly
For those who don't like to import exp packages, you can copy the source code:
// Keys returns the keys of the map m.
// The keys will be an indeterminate order.
func Keys[M ~map[K]V, K comparable, V any](m M) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
I made a sketchy benchmark on the three methods described in other responses.
Obviously pre-allocating the slice before pulling the keys is faster than appending, but surprisingly, the reflect.ValueOf(m).MapKeys() method is significantly slower than the latter:
❯ go run scratch.go
populating
filling 100000000 slots
done in 56.630774791s
running prealloc
took: 9.989049786s
running append
took: 18.948676741s
running reflect
took: 25.50070649s
Here's the code: https://play.golang.org/p/Z8O6a2jyfTH
(running it in the playground aborts claiming that it takes too long, so, well, run it locally.)
A nicer way to do this would be to use append:
keys = []int{}
for k := range mymap {
keys = append(keys, k)
}
Other than that, you’re out of luck—Go isn’t a very expressive language.
A generic version (go 1.18+) of Vinay Pai's answer.
// MapKeysToSlice extract keys of map as slice,
func MapKeysToSlice[K comparable, V any](m map[K]V) []K {
keys := make([]K, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
return keys
}
Visit https://play.golang.org/p/dx6PTtuBXQW
package main
import (
"fmt"
"sort"
)
func main() {
mapEg := map[string]string{"c":"a","a":"c","b":"b"}
keys := make([]string, 0, len(mapEg))
for k := range mapEg {
keys = append(keys, k)
}
sort.Strings(keys)
fmt.Println(keys)
}
There is a cool lib called lo
A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)
With this lib you could do many convinient operations like map, filter, reduce and more. Also there are some helpers for map type
Keys
Creates an array of the map keys.
keys := lo.Keys[string, int](map[string]int{"foo": 1, "bar": 2})
// []string{"bar", "foo"}
Values
Creates an array of the map values.
values := lo.Values[string, int](map[string]int{"foo": 1, "bar": 2})
// []int{1, 2}

How to do date/time comparison

Is there any options in doing date comparison in Go? I have to sort data based on date and time - independently. So I might allow an object that occurs within a range of dates so long as it also occurs within a range of times. In this model, I could not simply just select the oldest date, youngest time/latest date, latest time and Unix() seconds compare them. I'd really appreciate any suggestions.
Ultimately, I wrote a time parsing string compare module to check if a time is within a range. However, this is not faring to well; I've got some gaping issues. I'll post that here just for fun, but I'm hoping there's a better way to time compare.
package main
import (
"strconv"
"strings"
)
func tryIndex(arr []string, index int, def string) string {
if index <= len(arr)-1 {
return arr[index]
}
return def
}
/*
* Takes two strings of format "hh:mm:ss" and compares them.
* Takes a function to compare individual sections (split by ":").
* Note: strings can actually be formatted like "h", "hh", "hh:m",
* "hh:mm", etc. Any missing parts will be added lazily.
*/
func timeCompare(a, b string, compare func(int, int) (bool, bool)) bool {
aArr := strings.Split(a, ":")
bArr := strings.Split(b, ":")
// Catches margins.
if (b == a) {
return true
}
for i := range aArr {
aI, _ := strconv.Atoi(tryIndex(aArr, i, "00"))
bI, _ := strconv.Atoi(tryIndex(bArr, i, "00"))
res, flag := compare(aI, bI)
if res {
return true
} else if flag { // Needed to catch case where a > b and a is the lower limit
return false
}
}
return false
}
func timeGreaterEqual(a, b int) (bool, bool) {return a > b, a < b}
func timeLesserEqual(a, b int) (bool, bool) {return a < b, a > b}
/*
* Returns true for two strings formmated "hh:mm:ss".
* Note: strings can actually be formatted like "h", "hh", "hh:m",
* "hh:mm", etc. Any missing parts will be added lazily.
*/
func withinTime(timeRange, time string) bool {
rArr := strings.Split(timeRange, "-")
if timeCompare(rArr[0], rArr[1], timeLesserEqual) {
afterStart := timeCompare(rArr[0], time, timeLesserEqual)
beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
return afterStart && beforeEnd
}
// Catch things like `timeRange := "22:00:00-04:59:59"` which will happen
// with UTC conversions from local time.
// THIS IS THE BROKEN PART I BELIEVE
afterStart := timeCompare(rArr[0], time, timeLesserEqual)
beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
return afterStart || beforeEnd
}
So TLDR, I wrote a withinTimeRange(range, time) function but it's not working totally correctly. (In fact, mostly just the second case, where a time range crosses over days is broken. The original part worked, I just realized I'd need to account for that when making conversions to UTC from local.)
If there's a better (preferably built in) way, I'd love to hear about it!
NOTE:
Just as an example, I solved this issue in Javascript with this function:
function withinTime(start, end, time) {
var s = Date.parse("01/01/2011 "+start);
var e = Date.parse("01/0"+(end=="24:00:00"?"2":"1")+"/2011 "+(end=="24:00:00"?"00:00:00":end));
var t = Date.parse("01/01/2011 "+time);
return s <= t && e >= t;
}
However I really want to do this filter server-side.
Use the time package to work with time information in Go.
Time instants can be compared using the Before, After, and Equal
methods. The Sub method subtracts two instants, producing a Duration.
The Add method adds a Time and a Duration, producing a Time.
Play example:
package main
import (
"fmt"
"time"
)
func inTimeSpan(start, end, check time.Time) bool {
return check.After(start) && check.Before(end)
}
func main() {
start, _ := time.Parse(time.RFC822, "01 Jan 15 10:00 UTC")
end, _ := time.Parse(time.RFC822, "01 Jan 16 10:00 UTC")
in, _ := time.Parse(time.RFC822, "01 Jan 15 20:00 UTC")
out, _ := time.Parse(time.RFC822, "01 Jan 17 10:00 UTC")
if inTimeSpan(start, end, in) {
fmt.Println(in, "is between", start, "and", end, ".")
}
if !inTimeSpan(start, end, out) {
fmt.Println(out, "is not between", start, "and", end, ".")
}
}
For comparison between two times use time.Sub()
// utc life
loc, _ := time.LoadLocation("UTC")
// setup a start and end time
createdAt := time.Now().In(loc).Add(1 * time.Hour)
expiresAt := time.Now().In(loc).Add(4 * time.Hour)
// get the diff
diff := expiresAt.Sub(createdAt)
fmt.Printf("Lifespan is %+v", diff)
The program outputs:
Lifespan is 3h0m0s
http://play.golang.org/p/bbxeTtd4L6
For case when your interval's end date doesn't contains hours like
"from 2017-01-01 to whole day of 2017-01-16" it's better to adjust interval's end to midnight of the next day to include all milliseconds like this:
if now.After(start) && now.Before(end.Add(24 * time.Hour).Truncate(24 * time.Hour)) {
...
}
It's possible to compare date using int64 of Unix epoch with seconds granularity. If you need more exact comparison like milisecons or microseconds etc. I guess that
#Oleg Neumyvakin's answer is perfect.
if expirationDate.Unix() > time.Now().Unix() {
...
}
If you're interested in comparing whether a time is close to another for test purposes, you can use testify assert.WithinDuration for this. For example:
expectedTime := time.Now()
actualTime := expectedTime.Add(100*time.Millisecond)
assert.WithinDuration(t, expectedTime, actualTime, 1*time.Second) // pass
assert.WithinDuration(t, expectedTime, actualTime, 1*time.Millisecond) // fail
Otherwise the implementation of assert.WithinDuration can be re-used in your code to determine how close two times are (subtracting one date from the other gives the time difference):
func WithinDuration(expected, actual time.Time, delta time.Duration) bool {
dt := expected.Sub(actual)
return dt >= -delta && dt <= delta
}
Recent protocols prefer usage of RFC3339 per golang time package documentation.
In general RFC1123Z should be used instead of RFC1123 for servers that insist on that format, and RFC3339 should be preferred for new protocols. RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting; when used with time.Parse they do not accept all the time formats permitted by the RFCs.
cutOffTime, _ := time.Parse(time.RFC3339, "2017-08-30T13:35:00Z")
// POSTDATE is a date time field in DB (datastore)
query := datastore.NewQuery("db").Filter("POSTDATE >=", cutOffTime).
As explained in the theread we could use github.com/google/go-cmp/cmp package for dates comparison in tests.
func TestDates(t *testing.T) {
date, _ := time.Parse(time.RFC3339, "2021-11-05T12:00:00+02:00")
dateEqual, _ := time.Parse(time.RFC3339, "2021-11-05T11:00:00+01:00")
dateNotEqual, _ := time.Parse(time.RFC3339, "2021-11-05T12:00:01+02:00")
assertDates(t, date, dateEqual) //pass
assertDates(t, date, dateNotEqual) //fail
}
func assertDates(t *testing.T, expected, actual time.Time) {
t.Helper()
if diff := cmp.Diff(expected, actual); diff != "" {
t.Errorf("mismatch (-expected +actual):\n%s", diff)
}
}
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Hello World")
maxRep := 5
repPeroid := 6
expiry := maxRep * repPeroid
fmt.Println("Expiry: ", expiry)
fmt.Println(time.Now())
CorrIdtime := time.Now().Add(time.Second * time.Duration(expiry)).Format(time.RFC3339)
Notifytime := time.Now().Add(2 * time.Second * time.Duration(expiry)).Format(time.RFC3339)
fmt.Println(CorrIdtime)
fmt.Println(Notifytime)
if CorrIdtime < Notifytime {
fmt.Println("Discarded")
} else {
fmt.Println("Accepted")
}
}
Per proposal time: add Time.Compare and related commit, time.Compare will be added in the new release (Go 1.20)
// Compare compares the time instant t with u. If t is before u, it returns -1;
// if t is after u, it returns +1; if they're the same, it returns 0.
func (t Time) Compare(u Time) int {
Sample
var t1, t2 Time
result := t1.Compare(t2)

In Go how to get a slice of values from a map?

If I have a map m is there a better way of getting a slice of the values v than this?
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)
}
Is there a built-in feature of a map? Is there a function in a Go package, or is this the only way to do this?
As an addition to jimt's post:
You may also use append rather than explicitly assigning the values to their 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)
}
Note that the length is zero (no elements present yet) but the capacity (allocated space) is initialized with the number of elements of m. This is done so append does not need to allocate memory each time the capacity of the slice v runs out.
You could also make the slice without the capacity value and let append allocate the memory for itself.
Unfortunately, no. There is no builtin way to do this.
As a side note, you can omit the capacity argument in your slice creation:
v := make([]string, len(m))
The capacity is implied to be the same as the length here.
Go 1.18
You can use maps.Values from the golang.org/x/exp package.
Values returns the values of the map m. The values will be in an indeterminate order.
func main() {
m := map[int]string{1: "a", 2: "b", 3: "c", 4: "d"}
v := maps.Values(m)
fmt.Println(v)
}
The package exp includes experimental code. The signatures may or may not change in the future, and may or may not be promoted to the standard library.
If you don't want to depend on an experimental package, you can easily implement it yourself. In fact, this code is a copy-paste from the exp package:
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
}
Not necessarily better, but the cleaner way to do this is by defining both the Slice LENGTH and CAPACITY like 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)
}
Full example:
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
}
Simple solution but a lot of gotchas. Read this blog post for more details: https://web3.coach/golang-how-to-convert-map-to-slice-three-gotchas
As far as I'm currently aware, go doesn't have a way method for concatenation of strings/bytes in to a resulting string without making at least /two/ copies.
You currently have to grow a []byte since all string values are const, THEN you have to use the string builtin to have the language create a 'blessed' string object, which it will copy the buffer for since something somewhere could have a reference to the address backing the []byte.
If a []byte is suitable then you can gain a very slight lead over the bytes.Join function by making one allocation and doing the copy calls your self.
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)
}
As of 1.18, this is the best way:
https://stackoverflow.com/a/71635953/130427
Pre 1.18
You can use this maps package:
go get https://github.com/drgrib/maps
Then all you have to call is
values := maps.GetValuesIntString(m)
It's type-safe for that common map combination. You can generate other type-safe functions for any other type of map using the mapper tool in the same package.
Full disclosure: I am the creator of this package. I created it because I found myself rewriting these functions for map repeatedly.

Resources