Golang map put if absent? - go

I have a map of the format:
map[string]map[string]int
In this main map, I want to do something like putIfAbsent("key", new HashMap<>() as we have in Java. What is a clean and shorthand way to do it in Go?

You can do:
var val map[string]int
val, exists := m[key]
if !exists {
val = make(map[string]int)
m[key] = val
}
If you don't need the val in the code coming below this:
if _,exists := m[key]; !exists {
m[key]=make(map[string]int)
}

If you don't intend to use the value right away, here you go...
m := make(map[string]map[string]int)
if _, ok := m["unknown"]; !ok {
m["unknown"] = make(map[string]int)
}
Below is a suggestion for improvement:
To keep things clean and easy to understand, you can define your own types. For example, if your data is a mapping of "cities to persons to age", I would do it like this:
type Person map[string]int
type City map[string]Person
m := make(City)
if _, ok := m["Dhaka"]; !ok {
m["Dhaka"] = make(Person)
}

func main() {
var testMap map[int]interface{}
testMap = make(map[int]interface{})
var addMap map[int]string
addMap = make(map[int]string)
addMap[1] = "999"
addMap[2] = "888"
Add(testMap, 111, addMap)
for key, val := range testMap {
fmt.Println(key)
for key2, val2 := range val.(map[int]string) {
fmt.Println(key2, val2)
}
}
}
func Add(_testMap map[int]interface{}, _key int, _val map[int]string) {
_, exist := _testMap[_key] // _ -> value
if exist == false {
//addmap
_testMap[_key] = _val
} else {
//whatever wanna to do
}
}

Related

AWS dynamo stream processing using Go lambda

I'm using an AWS Lambda function written in Go with Dynamo stream but I don't see any way where I can marshall the old & new image to my struct because it's returning the image as map[string]DynamoDBAttributeValue.
I can check individual key and then assign it to my struct one by one, but is there an direct way to Marshall directly?
func HandleRequest(ctx context.Context, event events.DynamoDBEvent) {
for _, record := range event.Records {
var newStruct models.MyStruct // want to marshall newImage in this struct
logger.Debugf("New: %#v", record.Change.NewImage)
}
}
UPDATE:
Here is the custom DynamoDBEvent that I'm using now:
type DynamoDBEvent struct {
Records []DynamoDBEventRecord `json:"Records"`
}
type DynamoDBEventRecord struct {
AWSRegion string `json:"awsRegion"`
Change DynamoDBStreamRecord `json:"dynamodb"`
EventID string `json:"eventID"`
EventName string `json:"eventName"`
EventSource string `json:"eventSource"`
EventVersion string `json:"eventVersion"`
EventSourceArn string `json:"eventSourceARN"`
UserIdentity *events.DynamoDBUserIdentity `json:"userIdentity,omitempty"`
}
type DynamoDBStreamRecord struct {
ApproximateCreationDateTime events.SecondsEpochTime `json:"ApproximateCreationDateTime,omitempty"`
Keys map[string]*dynamodb.AttributeValue `json:"Keys,omitempty"`
NewImage map[string]*dynamodb.AttributeValue `json:"NewImage,omitempty"`
OldImage map[string]*dynamodb.AttributeValue `json:"OldImage,omitempty"`
SequenceNumber string `json:"SequenceNumber"`
SizeBytes int64 `json:"SizeBytes"`
StreamViewType string `json:"StreamViewType"`
}
Unfortunately there is no elegant way yet, sot you have to check each key/value pairs and assign them to your struct.
PS: As small sugar you can use func FromDynamoDBMap from this package and work with map[string]interface{} not with map[string]events.DynamoDBAttributeValue which is way easier.
I have created a solution for the problem. This was crucial to my implementation since my implementation uses custom keys.
Unfortunately, the Number type has to be cast to Float64 to make sure that it is not lost.
This will result in a completely usable struct against the changeset. You may need a map of your models and a known field
Usage
newResult, _ := UnmarshalStreamImage(record.Change.NewImage)
newMarshal, _ := attributevalue.MarshalMap(newResult)
model := SomeModel{}
err := attributevalue.UnmarshalMap(newMarshal, &model)
Function Code
// UnmarshalStreamImage converts events.DynamoDBAttributeValue to struct
func UnmarshalStreamImage(attribute map[string]events.DynamoDBAttributeValue) (map[string]interface{}, error) {
baseAttrMap := make(map[string]interface{})
for k, v := range attribute {
baseAttrMap[k] = extractVal(v)
}
return baseAttrMap, nil
}
func extractVal(v events.DynamoDBAttributeValue) interface{} {
var val interface{}
switch v.DataType() {
case events.DataTypeString:
val = v.String()
case events.DataTypeNumber:
val, _ = v.Float()
case events.DataTypeBinary:
val = v.Binary()
case events.DataTypeBoolean:
val = v.Boolean()
case events.DataTypeNull:
val = nil
case events.DataTypeList:
list := make([]interface{}, len(v.List()))
for _, item := range v.List() {
list = append(list, extractVal(item))
}
val = list
case events.DataTypeMap:
mapAttr := make(map[string]interface{}, len(v.Map()))
for k, v := range v.Map() {
mapAttr[k] = extractVal(v)
}
val = mapAttr
case events.DataTypeBinarySet:
set := make([][]byte, len(v.BinarySet()))
for _, item := range v.BinarySet() {
set = append(set, item)
}
val = set
case events.DataTypeNumberSet:
set := make([]string, len(v.NumberSet()))
for _, item := range v.NumberSet() {
set = append(set, item)
}
val = set
case events.DataTypeStringSet:
set := make([]string, len(v.StringSet()))
for _, item := range v.StringSet() {
set = append(set, item)
}
val = set
}
return val
}```

Using reflect to assign a typed value

I'm working on one of our system applications, specifically in the configuration file handling bits. We currently have 3 different places where a configuration file can be stored, and that can possibly be extended later. What I'm trying to do is simplify the way we need to add a new managed field.
The solution I have so far looks something like this:
package main
import (
"reflect"
"strconv"
"strings"
)
type Datastore interface{}
type MyInt struct {
intVal int
}
func NewMyInt(key string, dv int, db *Datastore) *MyInt {
// Do something here to construct MyInt
return &MyInt{intVal: dv}
}
type Config struct {
myInts map[string]*MyInt
// Tag is of form "<key in DB>:<default value>"
Value1 MyInt "value1_key:12345"
Value2 MyInt "value2_key:54321"
}
func NewConfig(db *Datastore) *Config {
c := &Config{
myInts: make(map[string]*MyInt),
}
cType := reflect.TypeOf(c)
for i := 0; i < cType.NumField(); i++ {
f := cType.Field(i)
if f.Name == "myInts" {
continue
}
tag := string(f.Tag)
fields := strings.Split(tag, ":")
switch f.Type.Name() {
case "myInt":
intVal, _ := strconv.Atoi(fields[1])
val := NewMyInt(fields[0], intVal, db)
c.myInts[fields[0]] = val
// How do I set the i'th field to this newly constructed value?
}
}
return c
}
So far I'm just missing this piece to do the assignment.
For this question, you can try
func NewConfig(db *Datastore) *Config {
c := &Config{
myInts: make(map[string]*MyInt),
}
cType := reflect.TypeOf(c).Elem() // have to use Elem() to get actual value
cValue := reflect.ValueOf(c).Elem()
for i := 0; i < cType.NumField(); i++ {
f := cType.Field(i)
if f.Name == "myInts" {
continue
}
tag := string(f.Tag)
fields := strings.Split(tag, ":")
switch f.Type.Name() {
case "MyInt":
intVal, _ := strconv.Atoi(fields[1])
val := NewMyInt(fields[0], intVal, db)
c.myInts[fields[0]] = val
// How do I set the i'th field to this newly constructed value?
cValue.Field(i).Set(reflect.ValueOf(val).Elem())
}
}
fmt.Println(c.Value1.intVal, c.Value2.intVal)
return c
}

Deserialising aws struct to string mapping

I'm trying to deserialize an AWS struct in go so that I can pass in parameters and output values based on those parameters, for example;
func DisplayResults(conf *config.Configuration,
regionalData []*ec2.DescribeInstancesOutput) {
log.Debug("Displaying results")
log.Debug("Table view [%v]", conf.Display)
for _, rv := range regionalData {
for _, reservation := range rv.Reservations {
for _, instance := range reservation.Instances {
var i map[string]interface{}
json.Unmarshal(instance, i)
}
}
}
}
I've also tried using:
"github.com/fatih/structs"
i := structs.Map(instance)
log.Print("%v", i)
however, I think that I need to somehow derefence the struct because my output looks like this:
RamdiskId:<nil> SriovNetSupport:<nil> VpcId:0xc420321ba0 State:map[Code:0xc42031ea08 Name:0xc420321890] VirtualizationType:0xc420321b90 CapacityReservationId:<nil> ClientToken:0xc420321560 HibernationOptions:map[Configured:0xc42031e96b] IamInstanceProfile:map[Arn:0xc4203215a0 Id:0xc4203215b0] ImageId:0xc4203215c0
I've also tried this:
func DisplayResults(conf *config.Configuration, regionalData []*ec2.DescribeInstancesOutput, parameters []string) {
log.Debug("Displaying results")
log.Debug("Table view [%v]", conf.Display)
for _, rv := range regionalData {
for _, reservation := range rv.Reservations {
for _, instance := range reservation.Instances {
y := deref(instance)
log.Print("%v", y["InstanceId"])
}
}
}
}
func deref(instance *ec2.Instance) ec2.Instance {
var i ec2.Instance
x := &i
*x = *instance
return i
}

Golang convert list objects to string

I have two structs:
type A struct {
BankCode string `json:"bankCode"`
BankName string `json:"bankName"`
}
And:
type B struct {
A
extra string `json:" extra"`
}
And two slices:
listsA []A and listsB []B
I want to get bankCodes from listA and listB. bankcodes only contains bankcodes. It is a []string
It will be so easy as using two function.
func getBankCodes(data []A) []string {
res := make([]string, len(data))
for i := 0; i < len(data); i++ {
res[i] = data[i].BankCode
}
return res
}
func getBankCodes(data []B) []string {
res := make([]string, len(data))
for i := 0; i < len(data); i++ {
res[i] = data[i].BankCode
}
return res
}
How to use one common function ?
Well the clean solution would be to use an interface, since go doesn't support classic inheritance, so something like []parentclass can't work. Interfaces however can only describe functions not a common field, so you have to implement a Getter (essentially).
// GetBankCoder provides a function that gives the BankCode
type GetBankCoder interface {
getBankCode() string
}
// implement GetBankCoder for A (and indirectly for B)
func (a A) getBankCode() string {
return a.BankCode
}
and make your getBankCodes work on that interface type, notice the parameter of the function as well as the statement inside the loop:
func getBankCodes(data []GetBankCoder) []string { // <-- changed
res := make([]string, len(data))
for i := 0; i < len(data); i++ {
res[i] = data[i].getBankCode() // <-- changed
}
return res
}
There are other solutions where the function parameter is of interface{} type and then reflection is used to assure you can actually do .BankCode, but I don't like those, as they are not adding more clarity either.
... However, I couldn't get the golang playground to make this work correctly without putting it into a []GetBankCoder var first, before giving it to the function.
banks := make([]GetBankCoder, 0)
banks = append(banks, A{ BankCode: "ABC", BankName: "ABC Bank"})
getBankCodes(banks)
You may use one common function like so:
func BankCodes(data interface{}) []string {
if reflect.TypeOf(data).Kind() != reflect.Slice {
panic("err: data is not slice")
}
slice := reflect.Indirect(reflect.ValueOf(data))
res := make([]string, slice.Len())
for i := 0; i < slice.Len(); i++ {
a := slice.Index(i).Interface().(BankCoder)
res[i] = a.Bankcode()
}
return res
}
Code (try on The Go Playground):
package main
import (
"fmt"
"reflect"
)
func main() {
bs := []B{B{A{"BC1", "BN"}, "e"}, B{A{"BC2", "BN"}, "e"}}
strs := BankCodes(bs)
fmt.Println(strs)
as := []A{A{"AC1", "BN"}, A{"AC2", "BN"}}
strs2 := BankCodes(as)
fmt.Println(strs2)
}
func BankCodes(data interface{}) []string {
if reflect.TypeOf(data).Kind() != reflect.Slice {
panic("err: data is not slice")
}
slice := reflect.Indirect(reflect.ValueOf(data))
res := make([]string, slice.Len())
for i := 0; i < slice.Len(); i++ {
a := slice.Index(i).Interface().(BankCoder)
res[i] = a.Bankcode()
}
return res
}
type A struct {
BankCode string `json:"bankCode"`
BankName string `json:"bankName"`
}
type B struct {
A
extra string `json:" extra"`
}
type BankCoder interface {
Bankcode() string
}
func (a A) Bankcode() string {
return a.BankCode
}

3 variable map in Go

I am trying to make a 3 variable map in go so that you can do something like.
var postlist = make(map[int][int]bool)
postlist[postid][userid] = true
if postid[postid][userid] = true {
//do something
}
I have tried to make my own using a struct like
var postlist = make(map[int]cusmap)
type cusmap struct {
userid int
seen bool
}
but then I don't know how to check to check both the userid and seen bool condition.
I am not sure what you are trying to do, but a map is only a key/value. You can't have key/key/value. In order to do this, you need the value to be a map, so you would do:
http://play.golang.org/p/dOAXNAI4CO
package main
func main() {
var postlist = make(map[int]map[int]bool)
postid, userid := 0, 0
postlist[postid] = make(map[int]bool)
postlist[postid][userid] = true
if postlist[postid][userid] == true {
println("ok")
} else {
println("ko")
}
}
If you want to implement a set of int pairs, you can use a structure as the map key:
type postData struct {
userID int
postID int
}
Then make a map with postData keys and bool values:
postSet := map[postData]bool{
postData{1234, 7284}: true,
postData{7777, 1212}: true}
We can exploit the fact that if we give a non-existent index to the map, it will just return a zero-value.
For bool, the zero-value is false.
if postSet[postData{7777, 1212}] {
fmt.Println("post found.")
} else {
fmt.Println("no such post!")
}
Here's a full working example: http://play.golang.org/p/VJw9Vm8gHA
An alternative to #creack's approach is to define funcs on the map itself that way you don't have to manually check every time you want to set / unset something:
func main() {
cm := CustMap{}
pid, uid := 0, 0
cm.Set(pid, uid)
fmt.Println(cm.Exists(pid, uid))
fmt.Println(cm.Exists(pid, 10))
fmt.Println(cm.Exists(10, 10))
}
type CustMap map[int]map[int]struct{} //struct makes the map use 0 bytes for values instead of 1 bytes for bools, handy as the map grows
func (cm CustMap) Set(pid, uid int) {
if _, ok := cm[pid]; !ok {
cm[pid] = make(map[int]struct{})
}
cm[pid][uid] = struct{}{}
}
func (cm CustMap) Exists(pid, uid int) (ok bool) {
if u := cm[pid]; u != nil {
_, ok = u[uid]
}
return
}
playground

Resources