Please see below my map
var romanNumeralDict map[int]string = map[int]string{
1000: "M",
900 : "CM",
500 : "D",
400 : "CD",
100 : "C",
90 : "XC",
50 : "L",
40 : "XL",
10 : "X",
9 : "IX",
5 : "V",
4 : "IV",
1 : "I",
}
I am looking to loop through this map in the order of the size of the key
for k, v := range romanNumeralDict {
fmt.Println("k:", k, "v:", v)
}
However, this prints out
k: 1000 v: M
k: 40 v: XL
k: 5 v: V
k: 4 v: IV
k: 900 v: CM
k: 500 v: D
k: 400 v: CD
k: 100 v: C
k: 90 v: XC
k: 50 v: L
k: 10 v: X
k: 9 v: IX
k: 1 v: I
Is there a way that I can print them out in the order of the size of the key so, I would like to loop through this map like this
k:1
K:4
K:5
K:9
k:10
etc...
Collect all keys, sort them and iterate your map by key, like the following:
keys := make([]int, 0)
for k, _ := range romanNumeralDict {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
fmt.Println(k, romanNumeralDict[k])
}
You can make it a little faster by preallocating keys because you know its length:
func sortedKeys(m map[Key]Value) ([]Key) {
keys := make([]Key, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Keys(keys)
return keys
}
Replace Key and Value with your key and value types (including the sort line). cough generics cough
Edit: Go 1.18 is finally getting generics! Here's the generic version:
// Ordered is a type constraint that matches any ordered type.
// An ordered type is one that supports the <, <=, >, and >= operators.
//
// Note the generics proposal suggests this type will be available from
// a standard "constraints" package in future.
type Ordered interface {
type int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64,
string
}
func sortedKeys[K Ordered, V any](m map[K]V) ([]K) {
keys := make([]K, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
return keys
}
Playground example
If execution speed isn't a big concern, you can get a sorted array of keys using MapKeys.
In this example, the keys are of type string:
keys := reflect.ValueOf(myMap).MapKeys()
keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) }
sort.Slice(keys, keysOrder)
// process map in key-sorted order
for _, key := range keys {
value := myMap[key.Interface().(string)]
fmt.Println(key, value)
}
See: Getting a slice of keys from a map
Warning: This bypasses some compile-time type-safety (panics if not a map)
You'll need to cast each key in order to get its raw value: key.Interface().(string)
You can iterate over the map in order by sorting the keys explicitly first, and then iterate over the map by key. Since you know the final size of keys from the romanNumeralDict outset, it is more efficient to allocate an array of the required size up front.
// Slice for specifying the order of the map.
// It is initially empty but has sufficient capacity
// to hold all the keys of the romanNumeralDict map.
keys := make([]int, 0, len(romanNumeralDict))
// Collect keys of the map
i := 0
for k, _ := range romanNumeralDict {
keys[i] = k
i++
}
// Ints sorts a slice of ints in increasing order
sort.Ints(keys)
// Iterate over the map by key with an order
for _, k := range keys {
fmt.Println(k, romanNumeralDict[k])
}
Based on #Brent's answer, I had an occasion where I wanted sorted map keys in a non-critical piece of code, without having to repeat myself too much. So here is a starting point to make a generic map-iteration function for many different types:
func sortedMapIteration(m interface{}, f interface{}) {
// get value and keys
val := reflect.ValueOf(m)
keys := val.MapKeys()
var sortFunc func(i, j int) bool
kTyp := val.Type().Key()
// determine which sorting function to use for the keys based on their types.
switch {
case kTyp.Kind() == reflect.Int:
sortFunc = func(i, j int) bool { return keys[i].Int() < keys[j].Int() }
case kTyp.Kind() == reflect.String:
sortFunc = func(i, j int) bool { return keys[i].String() < keys[j].String() }
}
sort.Slice(keys, sortFunc)
// get the function and call it for each key.
fVal := reflect.ValueOf(f)
for _, key := range keys {
value := val.MapIndex(key)
fVal.Call([]reflect.Value{key, value})
}
}
// example:
func main() {
sortedMapIteration(map[string]int{
"009": 9,
"003": 3,
"910": 910,
}, func(s string, v int) {
fmt.Println(s, v)
})
}
playground
To stress: this code is inefficient and uses reflection, so it does not have compile-time type safety, and a generic implementation should have more type safeguards and handle more key types. However, for quick and dirty scripts this can help you get started. You will need to add more cases to the switch block, according to which key types you expect to pass.
Related
I'd like to create a generic method to extract the keys from a map as an array - so that the type of that array should be the same as the type of the keys in the map. I know I want the usage to look something like this:
mapWithStringKeys := map[string]string {
"one": "1",
"ten": "10",
"hundred": "100",
}
mapWithIntKeys := map[int]string {
1: "one",
10: "ten",
100: "hundred",
}
keys(mapWithStringKeys) // []string{"one", "ten", "hundred"}
keys(mapWithStringKeys) // []int{1, 10, 100}
And the internal logic for extracting keys would look like this:
keys := make([]<TYPE>, 0, len(mymap))
for k := range mymap {
keys = append(keys, k)
}
I'm mostly having trouble defining the function so that golang knows how to "pass along" the types. I'm imagining something like this:
func keys[T infer](m map[T]any) []T {
keysArr := make([]T, 0, len(mymap))
for k := range mymap {
keysArr = append(keysArr, k)
}
return keysArr
}
But I can't really figure it out so I'm wondering if this is even possible, and if not, why?
You almost got it. Declare the key type as comparable and use a type constraint for the value type.
func keys[T comparable, V any](mymap map[T]V) []T {
keysArr := make([]T, 0, len(mymap))
for k := range mymap {
keysArr = append(keysArr, k)
}
return keysArr
}
To handle named map types, include a constraint for the map type with the ~ thing.
func keys[M ~map[T]V, T comparable, V any](mymap M) []T {
keysArr := make([]T, 0, len(mymap))
for k := range mymap {
keysArr = append(keysArr, k)
}
return keysArr
}
H/T to Burak for pointing out the existing library function.
I have created a generic data structure, with a name and a generic array in Golang.
package main
import "fmt"
type NamedArray struct {
Name string
values []interface{}
}
func main() {
data := [...]int{1, 2, 3, 4, 5}
interfaced_data := make([]interface{}, len(data))
for i, v := range data{
interfaced_data[i] = v
}
int_arr := NamedArray{Name: "Int Array", values: interfaced_data}
fmt.Println(int_arr)
// fmt.Println(int_arr.Sum()) -- uncomment to run Sum
data_float := [...]float64{0.1, 0.2, 0.3, 0.4, 0.5}
interfaced_data_float := make([]interface{}, len(data_float))
for i, v := range data_float{
interfaced_data_float[i] = v
}
float_arr := NamedArray{Name: "Float Array", values: interfaced_data_float}
fmt.Println(float_arr)
// fmt.Println(int_arr.Sum()) -- uncomment to run Sum
}
Now I want to define a method which allows me to sum all the values in the array. I know that they are numeric (though whether they are int or float is dependant on context) but I am having some serious trouble.
func (arr NamedArray) Sum() interface{} {
data := arr.values
sum := 0
for i, v := range data {
sum += v
}
return sum
}
I can't seem to make this work, though. When I uncomment lines 18 and 27 (fmt.Println(int_arr.Sum() and fmt.Println(int_arr.Sum()) and try to run the code I get
34:9: invalid operation: sum += v (mismatched types int and interface {})
During compilation.
Does anyone know how to add generic types, given we know that they are numeric?
Thanks!
The + operator is not defined on values of type interface{}. You have to get a value of type int out of the interface{} values before you can work with it as a number.
For that, you may use type assertion. See this example:
s := []interface{}{1, 2, 3, "invalid"}
sum := 0
for _, v := range s {
if i, ok := v.(int); ok {
sum += i
} else {
fmt.Println("Not int:", v)
}
}
fmt.Println("Sum:", sum)
Output (try it on the Go Playground):
Not int: invalid
Sum: 6
The above example only handles int numbers, and nothing else. If you want to "support" multiple number types, a more convenient way would be to use a type switch:
s := []interface{}{1, int32(2), int8(3), "invalid"}
sum := 0
for _, v := range s {
switch i := v.(type) {
case int:
sum += i
case int32:
sum += int(i)
case int8:
sum += int(i)
default:
fmt.Println("Not int:", v)
}
}
fmt.Println("Sum:", sum)
Output is the same. Try this one on the Go Playground.
I have a map[string]int
I want to get the x top values from it and store them in another data structure, another map or a slice.
From https://blog.golang.org/go-maps-in-action#TOC_7. I understood that:
When iterating over a map with a range loop, the iteration order is
not specified and is not guaranteed to be the same from one iteration
to the next.
so the result structure will be a slice then.
I had a look at several related topics but none fits my problem:
related topic 1
related topic 2
related topic 3
What would be the most efficient way to do this please?
Thanks,
Edit:
My solution would be to turn my map into a slice and sort it, then extract the first x values.
But is there a better way ?
package main
import (
"fmt"
"sort"
)
func main() {
// I want the x top values
x := 3
// Here is the map
m := make(map[string]int)
m["k1"] = 7
m["k2"] = 31
m["k3"] = 24
m["k4"] = 13
m["k5"] = 31
m["k6"] = 12
m["k7"] = 25
m["k8"] = -8
m["k9"] = -76
m["k10"] = 22
m["k11"] = 76
// Turning the map into this structure
type kv struct {
Key string
Value int
}
var ss []kv
for k, v := range m {
ss = append(ss, kv{k, v})
}
// Then sorting the slice by value, higher first.
sort.Slice(ss, func(i, j int) bool {
return ss[i].Value > ss[j].Value
})
// Print the x top values
for _, kv := range ss[:x] {
fmt.Printf("%s, %d\n", kv.Key, kv.Value)
}
}
Link to golang playground example
If I want to have a map at the end with the x top values, then with my solution I would have to turn the slice into a map again. Would this still be the most efficient way to do it?
Creating a slice and sorting is a fine solution; however, you could also use a heap. The Big O performance should be equal for both implementations (n log n) so this is a viable alternative with the advantage that if you want to add new entries you can still efficiently access the top N items without repeatedly sorting the entire set.
To use a heap, you would implement the heap.Interface for the kv type with a Less function that compares Values as greater than (h[i].Value > h[j].Value), add all of the entries from the map, and then pop the number of items you want to use.
For example (Go Playground):
func main() {
m := getMap()
// Create a heap from the map and print the top N values.
h := getHeap(m)
for i := 1; i <= 3; i++ {
fmt.Printf("%d) %#v\n", i, heap.Pop(h))
}
// 1) main.kv{Key:"k11", Value:76}
// 2) main.kv{Key:"k2", Value:31}
// 3) main.kv{Key:"k5", Value:31}
}
func getHeap(m map[string]int) *KVHeap {
h := &KVHeap{}
heap.Init(h)
for k, v := range m {
heap.Push(h, kv{k, v})
}
return h
}
// See https://golang.org/pkg/container/heap/
type KVHeap []kv
// Note that "Less" is greater-than here so we can pop *larger* items.
func (h KVHeap) Less(i, j int) bool { return h[i].Value > h[j].Value }
func (h KVHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h KVHeap) Len() int { return len(h) }
func (h *KVHeap) Push(x interface{}) {
*h = append(*h, x.(kv))
}
func (h *KVHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
In the language there is a minimum function https://golang.org/pkg/math/#Min But what if I have more than 2 numbers? I must to write a manual comparison in a for loop, or is there another way? The numbers are in the slice.
No, there isn't any better way than looping. Not only is it cleaner than any other approach, it's also the fastest.
values := []int{4, 20, 0, -11, -10}
min := values[0]
for _, v := range values {
if (v < min) {
min = v
}
}
fmt.Println(min)
EDIT
Since there has been some discussion in the comments about error handling and how to handle empty slices, here is a basic function that determines the minimum value. Remember to import errors.
func Min(values []int) (min int, e error) {
if len(values) == 0 {
return 0, errors.New("Cannot detect a minimum value in an empty slice")
}
min = values[0]
for _, v := range values {
if (v < min) {
min = v
}
}
return min, nil
}
General answer is: "Yes, you must use a loop, if you do not know exact number of items to compare".
In this package Min functions are implemented like:
// For 2 values
func Min(value_0, value_1 int) int {
if value_0 < value_1 {
return value_0
}
return value_1
}
// For 1+ values
func Mins(value int, values ...int) int {
for _, v := range values {
if v < value {
value = v
}
}
return value
}
You should write a loop. It does not make sense to create dozens of function in standard library to find min/max/count/count_if/all_of/any_of/none_of etc. like in C++ (most of them in 4 flavours according arguments).
How would you implement the deleteRecords function in the code below:
Example:
type Record struct {
id int
name string
}
type RecordList []*Record
func deleteRecords( l *RecordList, ids []int ) {
// Assume the RecordList can contain several 100 entries.
// and the number of the of the records to be removed is about 10.
// What is the fastest and cleanest ways to remove the records that match
// the id specified in the records list.
}
I did some micro-benchmarking on my machine, trying out most of the approaches given in the replies here, and this code comes out fastest when you've got up to about 40 elements in the ids list:
func deleteRecords(data []*Record, ids []int) []*Record {
w := 0 // write index
loop:
for _, x := range data {
for _, id := range ids {
if id == x.id {
continue loop
}
}
data[w] = x
w++
}
return data[:w]
}
You didn't say whether it's important to preserve the order of records in the list. If you don't then this function is faster than the above and still fairly clean.
func reorder(data []*Record, ids []int) []*Record {
n := len(data)
i := 0
loop:
for i < n {
r := data[i]
for _, id := range ids {
if id == r.id {
data[i] = data[n-1]
n--
continue loop
}
}
i++
}
return data[0:n]
}
As the number of ids rises, so does the cost of the linear search. At around 50 elements, using a map or doing a binary search to look up the id becomes more efficient, as long as you can avoid rebuilding the map (or resorting the list) every time. At several hundred ids, it becomes more efficient to use a map or a binary search even if you have to rebuild it every time.
If you wish to preserve original contents of the slice, something like this is more appropriate:
func deletePreserve(data []*Record, ids []int) []*Record {
wdata := make([]*Record, len(data))
w := 0
loop:
for _, x := range data {
for _, id := range ids {
if id == x.id {
continue loop
}
}
wdata[w] = x
w++
}
return wdata[0:w]
}
For a personal project, I did something like this:
func filter(sl []int, fn func(int) bool) []int {
result := make([]int, 0, len(sl))
last := 0
for i, v := range sl {
if fn(v) {
result = append(result, sl[last:i]...)
last = i + 1
}
}
return append(result, sl[last:]...)
}
It doesn't mutate the original, but should be relatively efficient.
It's probably better to just do:
func filter(sl []int, fn func(int) bool) (result []int) {
for _, v := range sl {
if !fn(v) {
result = append(result, v)
}
}
return
}
Simpler and cleaner.
If you want to do it in-place, you probably want something like:
func filter(sl []int, fn func(int) bool) []int {
outi := 0
res := sl
for _, v := range sl {
if !fn(v) {
res[outi] = v
outi++
}
}
return res[0:outi]
}
You can optimize this to use copy to copy ranges of elements, but that's twice
the code and probably not worth it.
So, in this specific case, I'd probably do something like:
func deleteRecords(l []*Record, ids []int) []*Record {
outi := 0
L:
for _, v := range l {
for _, id := range ids {
if v.id == id {
continue L
}
}
l[outi] = v
outi++
}
return l[0:outi]
}
(Note: untested.)
No allocations, nothing fancy, and assuming the rough size of the list of Records and the list of ids you presented, a simple linear search is likely to do as well as fancier things but without any overhead. I realize that my version mutates the slice and returns a new slice, but that's not un-idiomatic in Go, and it avoids forcing the slice at the callsite to be heap allocated.
For the case you described, where len(ids) is approximately 10 and len(*l) is in the several hundreds, this should be relatively fast, since it minimizes memory allocations by updating in place.
package main
import (
"fmt"
"strconv"
)
type Record struct {
id int
name string
}
type RecordList []*Record
func deleteRecords(l *RecordList, ids []int) {
rl := *l
for i := 0; i < len(rl); i++ {
rid := rl[i].id
for j := 0; j < len(ids); j++ {
if rid == ids[j] {
copy(rl[i:len(*l)-1], rl[i+1:])
rl[len(rl)-1] = nil
rl = rl[:len(rl)-1]
break
}
}
}
*l = rl
}
func main() {
l := make(RecordList, 777)
for i := range l {
l[i] = &Record{int(i), "name #" + strconv.Itoa(i)}
}
ids := []int{0, 1, 2, 4, 8, len(l) - 1, len(l)}
fmt.Println(ids, len(l), cap(l), *l[0], *l[1], *l[len(l)-1])
deleteRecords(&l, ids)
fmt.Println(ids, len(l), cap(l), *l[0], *l[1], *l[len(l)-1])
}
Output:
[0 1 2 4 8 776 777] 777 777 {0 name #0} {1 name #1} {776 name #776}
[0 1 2 4 8 776 777] 772 777 {1 name #1} {3 name #3} {775 name #775}
Instead of repeatedly searching ids, you could use a map. This code preallocates the full size of the map, and then just moves array elements in place. There are no other allocations.
func deleteRecords(l *RecordList, ids []int) {
m := make(map[int]bool, len(ids))
for _, id := range ids {
m[id] = true
}
s, x := *l, 0
for _, r := range s {
if !m[r.id] {
s[x] = r
x++
}
}
*l = s[0:x]
}
Use the vector package's Delete method as a guide, or just use a Vector instead of a slice.
Here is one option but I would hope there are cleaner/faster more functional looking ones:
func deleteRecords( l *RecordList, ids []int ) *RecordList {
var newList RecordList
for _, rec := range l {
toRemove := false
for _, id := range ids {
if rec.id == id {
toRemove = true
}
if !toRemove {
newList = append(newList, rec)
}
}
return newList
}
With large enough l and ids it will be more effective to Sort() both lists first and then do a single loop over them instead of two nested loops