So lets say I have a map of interface like this:
c := map[string]interface{} {
"test": test,
"test2": test2,
}
Assuming that test is a map[string]map[string]map[string]string and test2 is map[string]string.
How would I create a for loop that would enumerate each index of the map and would enumerate through each index's map also?
So far I have gotten:
func sanitize_map(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
//Here is where I want to enumerate through the map of k
}
return m
}
No need for reflection; use a type assertion and pass the value back to your sanitize function
func sanitizeMap(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
_ = k
if v, ok := v.(map[string]interface{}); ok {
sanitizeMap(v)
}
}
return m
}
You can use reflect:
import "reflect"
func sanitize_map(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
// Do something with the key k
kind := reflect.ValueOf(v).Kind()
if kind == reflect.Map {
// You have to be sure the value is of type map[string]interface{}
newValue := v.(map[string]interface{})
// recursively call sanitize
sanitize_map(newValue)
}
}
return m
}
The carveat is: every value in the map has to be either not a map (atom) or a map[string]interface{}. Note map[string]interface{} and map[string]map[string]interface{} are completely unrelated types, and you cannot use a type assertion of the second type on the first one.
However, you can put a map[string]map[string]string in a map[string]interface{}, like this:
innerMap1 := make(map[string]interface{})
// map to strings
innerMap1["a"] = "String 1"
innerMap2 := make(map[string]interface{})
// set mappings, maybe to other types
outerMap := make(map[string]interface{})
outerMap["ABC"] = innerMap1
outerMap["DEF"] = innerMap2
Now you can pass outerMap to the function, and reflect will automatically "strip" the layers of maps for you.
Related
I have a use case where the order of objects needs to be in a specific order. The current implementation is done with using map and I've found numerous posts and articles where it states that map are an unordered list. All of the solutions that I found are those where they've made the keys as integers and they've used sort.Ints(keys) to sort by keys.
In the code, I'm using a yaml template to instantiate a dictionary pair, then passing it into the ProcessFruits function where it does the logic.
How would I go about getting the desired result (see below) where the object from the top of the list in fruits.yml.tmpl will always be first?
Here's a simplified version of my code:
//Filename: fruits.yml.tmpl
fruits: {{ $fruits := processFruits
"oranges" true
"bananas" false
"apples" true
}}
{{ $fruits }}
//Filename: fruits.go
func ProcessFruits(fruits map[string]interface{}) (interface{}) {
keys := make([]string, len(fruits))
i := 0
for fruit := range fruits {
keys[i] = fruit
i++
}
sort.Strings(keys)
fmt.Println(keys)
}
// Connect fruits.yml.tmpl to the ProcessFruits function
tmpl, err := template.New(t).Funcs(template.FuncMap(map[string]interface{}{
"processFruits": ProcessFruits,
})).Funcs(sprig.TxtFuncMap())
Actual Results:
[apples:true bananas:false oranges:true]
Desired Results:
[oranges:true bananas:false apples:true]
Go Playground
https://go.dev/play/p/hK2AdRVsZXJ
You are missing the usage of sort.Reverse() and sort.StringSlice()
func main() {
keys := []string{"bananas", "apples", "oranges"}
sort.Sort(sort.Reverse(sort.StringSlice(keys)))
fmt.Println(keys)
}
https://go.dev/play/p/n08S7xtbeij
See: https://pkg.go.dev/sort#example-Reverse
The arguments are passed as a slice. Collect every other argument as a string and print:
func ProcessFruits(args ...interface{}) interface{} {
var fruits []string
for i, arg := range args {
if i%2 == 0 {
fruits = append(fruits, arg.(string))
}
}
fmt.Println(fruits)
return nil
}
Not the prettiest solution, but I think I've figured out a working code to my problem. What I've done was creating another dictionary that will keep track of the order of the "fruits", then combining the two dictionary together with a nested for loop and output the result to a slice.
Here's my code:
package main
import (
"fmt"
"sort"
)
func ProcessFruits(fruits map[string]interface{}, counts map[int]string) {
keys := make([]string, len(fruits))
var foo []string
var baz []int
for k := range fruits {
foo = append(foo, k)
}
for _, k := range foo {
fmt.Println("Key-string:", k, "Value-bool:", fruits[k])
}
fmt.Println("==========================")
// Iterate over counts (keys are ints)
for l := range counts {
baz = append(baz, l)
}
sort.Ints(baz)
for _, l := range baz {
fmt.Println("Key-int:", l, "Value-string:", counts[l])
}
fmt.Println("==========================")
// Correlate list with sorted integer keys with the other list that contains true/false
i := 0
for _, m := range baz {
for _, n := range foo {
//fmt.Println("Key-int:", m, "Value-string:", counts[m])
//fmt.Println("Key-string:", n, "Value-bool:", fruits[n])
if counts[m] == n {
keys[i] = n
i++
//fmt.Println(i)
}
}
}
// Desired results is now in the slice, keys.
fmt.Println(keys)
}
func main() {
var m = map[string]interface{}{
"oranges": true,
"bananas": false,
"apples": true,
}
var n = map[int]string{
0: "oranges",
1: "bananas",
2: "apples",
}
ProcessFruits(m, n)
}
If anyone has a better solution, then I'd be curious to know.
I have something like
type rateLimit struct {
Count int
}
type RateLimiter struct {
cachePeriod time.Duration
rateLimits map[string]*rateLimit
}
And a function like this:
func NewRateLimiter(////) *RateLimiter {
rateLimits := make(map[string]*rateLimit)
return &RateLimiter{
cachePeriod: cachePeriod,
rateLimits: rateLimits,
}
}
This structure allows for you to create a nested structure like
rateLimits["foo"] = a
but I'm trying to make changes so that it is nested and I can do rateLimits["foo"]["bar"] = a
I tried changing the structure to be
rateLimits := make(map[string]map[string]*rateLimit)
and
type rateLimiter struct {
cachePeriod time.Duration
rateLimits map[string]map[string]*rateLimit
}
but I'm getting panic: assignment to entry in nil map [recovered] panic: assignment to entry in nil map
rateLimits := make(map[string]map[string]*rateLimit)
rateLimits["foo"] = make(map[string]*rateLimit)
rateLimits["foo"]["bar"] = a
rateLimits := map[string]map[string]*rateLimit{"foo": {"bar": a}}
You must make the nested map:
func setNested(m1 map[string]map[string]string, k1, k2 string, v string) {
m2 := m1[k1]
if m2 == nil {
m2 = make(map[string]string)
m1[k1] = m2
}
m2[k2] = v
}
Use it like this:
setNested(rateLimits, "foo", "bar", a)
I'd like to be able to write a GoLang function to take an array of keys and a value (i.e. keys={"a", "b", "c"}, value=123) then build a data-structure of nested maps where the positional index in the array corresponds to the depth in the nested map, and the value is assigned to the last key. For example, given the above keys and value, I want to build the following dictionary structure
{"a":{"b":{"c":123}}}
Below is the code I currently have. The issue is that the resulting map is as follows
{"a":{}, "b":{}, "c":123}.
Any advice as to how I should amend this/why it's happening would be much appreciated.
import (
"fmt"
)
type dict map[interface{}]interface{}
func main() {
vals := []interface{}{"a", "b", "c"}
// create a dictionary
d := make(dict)
d.Set(vals, 123)
// print it
fmt.Println(d)
}
func (d dict) Set(keys []interface{}, value interface{}) {
d2 := d
fmt.Println("Initial dict: ", d2)
keylen := len(keys)-1
for _, key := range keys[:keylen] {
// if key exists, recurse into that part of the dict
if entry, ok := d2[key]; ok {
d2 := entry
fmt.Println("Entered level in dict: ", d2)
} else {
d3 := make(dict)
d2[key] = d3
d2 := d3
fmt.Println("Created new level in dict: ", d2)
}
}
d2[keys[keylen]] = value
fmt.Println("Final dict: ", d2)
}
You seem to have overcomplicated your solution. This recursive algorithm should do:
func set(d dict,keys []interface{}, value interface{}) {
if len(keys)==1 {
d[keys[0]]=value
return
}
v, ok:=d[keys[0]]
if !ok {
v=dict{}
d[keys[0]]=v
}
set(v.(dict),keys[1:],value)
}
You have to add code to handle the case where you're resetting a value (i.e. when v.(dict) type assertion might fail). Otherwise, you can recursively descend the map, and consume the keys at the same time.
I am trying to create a function that could accept following
*struct
[]*struct
map[string]*struct
Here struct could be any struct not just a specific one.
Converting interface to *struct or []*struct is working fine.
But giving error for map.
After reflect it shows it is map[] but giving error when try to iterate over range.
Here is code
package main
import (
"fmt"
"reflect"
)
type Book struct {
ID int
Title string
Year int
}
func process(in interface{}, isSlice bool, isMap bool) {
v := reflect.ValueOf(in)
if isSlice {
for i := 0; i < v.Len(); i++ {
strct := v.Index(i).Interface()
//... proccess struct
}
return
}
if isMap {
fmt.Printf("Type: %v\n", v) // map[]
for _, s := range v { // Error: cannot range over v (type reflect.Value)
fmt.Printf("Value: %v\n", s.Interface())
}
}
}
func main() {
b := Book{}
b.Title = "Learn Go Language"
b.Year = 2014
m := make(map[string]*Book)
m["1"] = &b
process(m, false, true)
}
Is there any way to convert interface{} to map and iterate or get it's elements.
If the map value can be any type, then use reflect to iterate through the map:
if v.Kind() == reflect.Map {
for _, key := range v.MapKeys() {
strct := v.MapIndex(key)
fmt.Println(key.Interface(), strct.Interface())
}
}
playground example
If there's a small and known set of struct types, then a type switch can be used:
func process(in interface{}) {
switch v := in.(type) {
case map[string]*Book:
for s, b := range v {
// b has type *Book
fmt.Printf("%s: book=%v\n" s, b)
}
case map[string]*Author:
for s, a := range v {
// a has type *Author
fmt.Printf("%s: author=%v\n" s, a)
}
case []*Book:
for i, b := range v {
fmt.Printf("%d: book=%v\n" i, b)
}
case []*Author:
for i, a := range v {
fmt.Printf("%d: author=%v\n" i, a)
}
case *Book:
fmt.Ptintf("book=%v\n", v)
case *Author:
fmt.Printf("author=%v\n", v)
default:
// handle unknown type
}
}
You don't need reflect here. Try:
v, ok := in.(map[string]*Book)
if !ok {
// Can't assert, handle error.
}
for _, s := range v {
fmt.Printf("Value: %v\n", s)
}
Same goes for the rest of your function. It looks like you're using reflection when you would be better served by a type switch.
Alternatively, if you insist on using reflection here (which doesn't make a lot of sense) you can also use Value.MapKeys with the result from your ValueOf (see the answer https://stackoverflow.com/a/38186057/714501)
This may help:
b := []byte(`{"keyw":"value"}`)
var f interface{}
json.Unmarshal(b, &f)
myMap := f.(map[string]interface{})
fmt.Println(myMap)
Another way to convert an interface{} into a map with the package reflect is with MapRange.
I quote:
MapRange returns a range iterator for a map. It panics if v's Kind is
not Map.
Call Next to advance the iterator, and Key/Value to access each entry.
Next returns false when the iterator is exhausted. MapRange follows
the same iteration semantics as a range statement.
Example:
iter := reflect.ValueOf(m).MapRange()
for iter.Next() {
key := iter.Key().Interface()
value := iter.Value().Interface()
...
}
I can create a "static" map via
type m map[int]map[int]map[int]bool
but the length of "keys" will be dynamic:
|---unknown len--|
m[1][2][3][4][2][0] = true
or
|---unk len--|
m[1][2][3][4] = true
How I can create this map in Go? Or any way exists?
Added: Hierarchical is IMPORTANT
Thanks in advance!
The map type:
A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type.
A map type must have a specific value type and a specific key type. What you want does not qualify for this: you want a map where the value is sometimes another map (of the same type), and sometimes it's a bool.
Your options:
1. With a wrapper value type
The idea here is to not use just a simple (bool) value type, but a wrapper which holds both of your potential values: both a map and the simple value (bool):
type Value struct {
Children MapType
V bool
}
type MapType map[int]*Value
var m MapType
This is basically what user3591723 suggested, so I won't detail it further.
2. With a tree
This is a variant of #1, but this way we clearly communicate it's a tree.
The cleanest way to implement your hierarchical structure would be to use a tree, where a node could look like this:
type KeyType int
type ValueType string
type Node struct {
Children map[KeyType]*Node
Value ValueType
}
This has the advantage that you may choose the value type (which is bool in your case, but you can change it to whatever type - I used string for presentation).
For easily build / manage your tree, we can add some methods to our Node type:
func (n *Node) Add(key KeyType, v ValueType) {
if n.Children == nil {
n.Children = map[KeyType]*Node{}
}
n.Children[key] = &Node{Value: v}
}
func (n *Node) Get(keys ...KeyType) *Node {
for _, key := range keys {
n = n.Children[key]
}
return n
}
func (n *Node) Set(v ValueType, keys ...KeyType) {
n = n.Get(keys...)
n.Value = v
}
And using it: 1. build a tree, 2. query some values, 3. change a value:
root := &Node{Value: "root"}
root.Add(0, "first")
root.Get(0).Add(9, "second")
root.Get(0, 9).Add(3, "third")
root.Get(0).Add(4, "fourth")
fmt.Println(root)
fmt.Println(root.Get(0, 9, 3))
fmt.Println(root.Get(0, 4))
root.Set("fourthMod", 0, 4)
fmt.Println(root.Get(0, 4))
Output (try it on the Go Playground):
&{map[0:0x104382f0] root}
&{map[] third}
&{map[] fourth}
&{map[] fourthMod}
3. With a recursive type definition
It may be surprising but it is possible to define a map type in Go which has unlimited or dynamic "depth", using a recursive definition:
type X map[int]X
It is what it says: it's a map with int keys, and values of the same type as the map itself.
The big downside of this recursive type is that it can't store any "useful" data in the value type. It can only store the "fact" whether a value is present which is identical to a bool-like information (bool type: true or false), which may be enough in rare cases, but not in most.
Let's see an example building a "tree":
var x X
x = map[int]X{}
x[0] = map[int]X{}
x[0][9] = map[int]X{}
x[0][9][3] = map[int]X{}
x[0][4] = map[int]X{}
fmt.Println(x)
Output:
map[0:map[9:map[3:map[]] 4:map[]]]
If we want to test if there is a "value" based on a series of keys, we have 2 options: either use the special v, ok := m[i] indexing (which reports if a value for the specified key exists), or test if the value is not nil, e.g. m[i] != nil.
Let's see some examples testing the above built map:
var ok bool
_, ok = x[0][9][3]
fmt.Println("x[0][9][3] exists:", ok, "; alternative way:", x[0][9][3] != nil)
_, ok = x[0][9][4]
fmt.Println("x[0][9][4] exists:", ok, "; alternative way:", x[0][9][4] != nil)
_, ok = x[0][4]
fmt.Println("x[0][4] exists:", ok, "; alternative way:", x[0][4] != nil)
_, ok = x[0][4][9][9][9]
fmt.Println("x[0][4][9][9][9] exists:", ok, "; alternative way:", x[0][4][9][9][9] != nil)
Output:
x[0][9][3] exists: true ; alternative way: true
x[0][9][4] exists: false ; alternative way: false
x[0][4] exists: true ; alternative way: true
x[0][4][9][9][9] exists: false ; alternative way: false
Try these on the Go Playground.
Note: Even though x[0][4] is the last "leaf", indexing further like x[0][4][9][9][9] will not cause a panic as a nil map can be indexed and yields the zero value of the value type (which is nil in case the value type is a map type).
Ok I had some fun playing with this a bit. Here is a much better implementation than what I did before:
type mymap map[int]*myentry
type myentry struct {
m mymap
b bool
}
func (mm mymap) get(idx ...int) *myentry {
if len(idx) == 0 {
return nil
}
entry, ok := mm[idx[0]]
if !ok {
return nil
} else if len(idx) == 1 {
return entry
}
for i := 1; i < len(idx); i++ {
if entry == nil || entry.m == nil {
return nil
}
entry = entry.m[idx[i]]
}
return entry
}
func (mm mymap) setbool(v bool, idx ...int) {
if len(idx) == 0 {
return
}
if mm[idx[0]] == nil {
mm[idx[0]] = &myentry{m: make(mymap), b: false}
} else if mm[idx[0]].m == nil {
mm[idx[0]].m = make(mymap)
}
if len(idx) == 1 {
mm[idx[0]].b = v
return
}
entry := mm[idx[0]]
for i := 1; i < len(idx); i++ {
if entry.m == nil {
entry.m = make(mymap)
entry.m[idx[i]] = &myentry{m: make(mymap), b: false}
} else if entry.m[idx[i]] == nil {
entry.m[idx[i]] = &myentry{m: make(mymap), b: false}
}
entry = entry.m[idx[i]]
}
entry.b = v
}
func (m mymap) getbool(idx ...int) bool {
if val := m.get(idx...); val != nil {
return val.b
}
return false
}
func (m mymap) getmap(idx ...int) mymap {
if val := m.get(idx...); val != nil {
return val.m
}
return nil
}
Playground link
Something like that ought to get you started
If you don't need the hierarchical map structure and just want to use keys with variable length one approach could be to simply use strings as keys and one single map.
m := make(map[string]bool)
k := fmt.Sprintf("%v_%v_%v", 1, 2, 3)
m[k] = true
fmt.Println(m[k])
You cannot do this as this sort of type is not representable in Go's type system.
You will have to redesign.
E.g. a type arbitrarilyKeyedMapwith a method lookup(vals ...int) bool.
Probably you'll need methods for setting and deletion too.