I need to check if the same key exists in two maps:
if v1, ok1 := map1["aaa"]; ok1 {
...
}
if v2, ok2 := map2["aaa"]; ok2 {
...
}
Is it possible to join these two conditions into one? I managed to do something like this:
v1, ok1 := map1["aaa"]
v2, ok2 := map2["aaa"]
if ok1 && ok2 {
...
}
but I'm curious whether it (assigning and checking) can be done in one if condition.
No, it can't be done. Spec: Index expressions:
An index expression on a map a of type map[K]V used in an assignment or initialization of the special form
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.
So you can use the special v, ok := m[k] form only if nothing else gets assigned.
However, if you don't use the zero value of the value type of the map, you can do the check using a simple tuple-assignment; by not using the special form but 2 simple index expressions.
For example if your value type is some interface type (e.g. interface{}), and you know you don't use the nil value, you may do the following:
if v1, v2 := m1["aaa"], m2["aaa"]; v1 != nil && v2 != nil {
fmt.Printf("Both map contains key '%s': %v, %v\n", "aaa", v1, v2)
}
Of course with a helper function, you can do it in one step:
func idx(m1, m2 map[string]interface{}, k string) (
v1, v2 interface{}, ok1, ok2 bool) {
v1, ok1 = m1[k]
v2, ok2 = m2[k]
return
}
Using it:
if v1, v2, ok1, ok2 := idx(m1, m2, "aaa"); ok1 && ok2 {
fmt.Printf("Both map contains key '%s': %v, %v\n", "aaa", v1, v2)
}
Try the examples on the Go Playground.
you could also use variadic parameter(three dots) to check multiple keys :
// check map 1 and map2 is null or not
func checkMap(m1, m2 map[string]interface{}, keys ...string) []bool {
var isExist []bool
for key := range keys {
//checking value from map 1
_, ok := m1[keys[key]]
if ok {
isExist = append(isExist, true) // append for the first time to avoid panic
} else {
isExist = append(isExist, false) // append for the first time to avoid panic
}
// checking value from map2
_, ok = m2[keys[key]]
if ok {
isExist[key] = true
} else {
isExist[key] = false
}
}
return isExist
}
And then you can check your keys in order like this :
result := checkMap(myMap, myMap2, "a", "b", "c", "d", "e", "f", "g")
fmt.Printf("result = %+v\n", result) // print the result
if result[0] {
fmt.Println("key a exist in both map")
}
if result[1] {
fmt.Println("key b exist in both map")
}
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'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 have a map[string]string and I need to test if some keys exist and if they do, convert some of the values to integers. For example:
m := map[string]string{"a": "b", "c": "d", "e": "f"}
if v1, ok := m["a"]; ok {
if v2, ok := m["c"]; ok {
if i1, err := strconv.Atoi(v1); err != nil {
if i2, err := strconv.Atoi(v2); err != nil {
// do something with i1, i2
}
}
}
}
I find myself thinking in the lines of:
if m.exists("a") && m.exists("c") && is_int(m["a"]) && is_int(m["c"]) {
// do something with atoi(m["a"]) and atoi(m["c"])
}
... but that's not convenient with Go's standard library. So, what is the Go way of doing it?
Testing if a string value is a number and converting it to a number is basically the same amount of work.
So I would rather use the following util function which besides checking the keys and testing the values also returns the converted numbers:
func getInts(m map[string]string, keys ...string) (is []int, err error) {
for _, k := range keys {
v, ok := m[k]
if !ok {
return is, fmt.Errorf("%s is missing", k)
}
var i int
if i, err = strconv.Atoi(v); err != nil {
return
}
is = append(is, i)
}
return
}
Testing it with 3 different cases:
maps := []map[string]string{
{},
{"a": "b", "c": "d", "e": "f"},
{"a": "1", "c": "2", "e": "f"},
}
for _, m := range maps {
if is, err := getInts(m, "a", "c"); err == nil {
fmt.Println("Numbers:", is)
} else {
fmt.Println("Error:", err)
}
}
Output (try it on the Go Playground):
Error: a is missing
Error: strconv.Atoi: parsing "b": invalid syntax
Numbers: [1 2]
One way to do it is to create your own map[string]string type and add some simple methods to that. Here is a simple example:
type mapExists map[string]string
func (m mapExists) exists(key string) bool {
_, ok := m[key]
return ok
}
func (m mapExists) isInt(key string) bool {
v, ok := m[key]
if !ok {
return false
}
_, err := strconv.ParseInt(v, 10, 64)
return err == nil
}
You can convert a map[string]string to the mapExists with:
m := map[string]string{
"a": "b",
"c": "2",
}
m2 := mapExists(m)
And then call stuff like in your example:
fmt.Println("a exists:\t", m2.exists("a"))
fmt.Println("asd exists:\t", m2.exists("asd"))
fmt.Println("a isInt:\t", m2.isInt("a"))
fmt.Println("c isInt:\t", m2.isInt("c"))
Which will output:
a exists: true
asd exists: false
a isInt: false
c isInt: true
Whether this is the "best" or "idiomatic" way is a matter of opinion and depends on the circumstances. In general, I would (personally) prefer to not use these wrappers. Your above example could be rewritten to something like the following :
func test() {
m := map[string]string{"a": "b", "c": "d", "e": "f"}
v1, v1ok := m["a"]
v2, v2ok := m["c"]
if !v1ok || !v2ok {
return
}
i1, err := strconv.Atoi(v1)
if err != nil {
return
}
i2, err := strconv.Atoi(v2) {
if err != nil {
return
}
}
Which I think is much more readable. It's more verbose, but Go is a verbose language ;-)
But again, it depends on the entirety of your codebase. If you're calling this pattern very often then a custom type might be a good solution (at the cost of slightly lower performance, which may or may not be a problem).
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.