3 variable map in Go - 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

Related

What is the right way to conditionally assign multiple properties to a struct

I'm working on a resolver function for a GraphQL query for a BE I'm writing in Go. In the resolver, I have user data that I want to update, using an input value containing several possible update properties.
In JavaScript, this can be done quickly through destructuring (pseudo):
const mergedObj = {...oldProps, ...newProps}
For now, my resolver function looks like this (using gqlgen for GraphQL Go resolvers):
func (r *mutationResolver) ModifyUser(ctx context.Context, input *model.ModifyUserInput) (*model.User, error) {
id := input.ID
us, ok := r.Resolver.UserStore[id]
if !ok {
return nil, fmt.Errorf("not found")
}
if input.FirstName != nil {
us.FirstName = *input.FirstName
}
if input.LastName != nil {
us.LastName = *input.LastName
}
if input.ProfileImage != nil {
us.ProfileImage = input.ProfileImage
}
if input.Password != nil {
us.Password = *input.Password
}
if input.Email != nil {
us.Email = *input.Email
}
if input.InTomorrow != nil {
us.InTomorrow = input.InTomorrow
}
if input.DefaultDaysIn != nil {
us.DefaultDaysIn = input.DefaultDaysIn
}
r.Resolver.UserStore[id] = us
return &us, nil
}
This feels quite boilerplatey. Would it make sense in this situation to iterate through struct keys? Or is there another pattern I'm missing?
Use a function to reduce the boilerplate:
func mergef[T any](a, b *T) {
if b != nil {
*a = *b
}
}
...
mergef(&us.FirstName, input.FirstName)
mergef(&us.LastName, input.LastName)
...
Use the reflect package to reduce more boilerplate:
// merge sets fields in struct pointed to by d to
// dereferenced fields in struct pointed to by s.
//
// Argument s must point to a struct with pointer type
// fields.
// Argument d must point to a struct with fields that
// correspond to the fields in s: there must be a field
// in d with the same name as a field in s; the type of
// the field in s must be a pointer to the type of the field
// in d.
func merge(d, s any) {
sv := reflect.ValueOf(s).Elem()
dv := reflect.ValueOf(d).Elem()
for i := 0; i < sv.NumField(); i++ {
sf := sv.Field(i)
if sf.IsNil() {
continue
}
df := dv.FieldByName(sv.Type().Field(i).Name)
df.Set(sf.Elem())
}
}
Employ the function like this:
merge(us, input)

Hashing reflect.Type

I'm trying to find a quick way of performing comparisons between two []reflect.Type. Right now I have the following:
func Equal(left, right []reflect.Type) bool {
if len(left) != len(right) {
return false
}
for i := 0; i < len(left); i++ {
if left[i] != right[i] {
return false
}
}
return true
}
Most of the slices don't change. So if I can find a way to hash them, I'd get a huge perf boost.
Background
I'm trying to (for fun) implement a form of function overloading in Go using the reflect package. The first thing I did is to convert each specialised/overloaded function into a signature type.
type Signature struct {
Variadic bool
In, Out []reflect.Type
}
The idea is that, when the overloaded function gets called, I'll convert the arguments into a slice of reflect.Type and then find a Signature where the In types match.
This works, but for each comparison, it's a linear scan which is pretty slow. If I could hash the slice of []reflect.Type I could stick that in a map and get constant time lookups.
I ended up abusing the built-in map to assign unique ids to each reflect.Type. Then I hash those using djb2.
type TypeCode struct {
seq int64
codes map[reflect.Type]int64
}
func (td *TypeCode) TypeID(t reflect.Type) int64 {
if code, ok := td.codes[t]; ok {
return code
}
td.seq++
td.codes[t] = td.seq
return td.seq
}
func (td *TypeCode) SliceTypeID(tt []reflect.Type) int64 {
id := int64(5381)
for _, t := range tt {
id = ((id << 5) + id) + td.TypeID(t)
}
return id
}
edit: I switched to a string based approach which is less efficient, but removes any potential for collisions.
type TypeCode struct {
seq int64
codes map[reflect.Type]string
}
func (td *TypeCode) TypeID(t reflect.Type) string {
if code, ok := td.codes[t]; ok {
return code
}
td.seq++
id := strconv.FormatInt(td.seq, 10)
td.codes[t] = id
return id
}
func (td *TypeCode) SliceTypeID(tt []reflect.Type) string {
ids := make([]string, len(tt))
for i := 0; i < len(tt); i++ {
ids[i] = td.TypeID(tt[i])
}
return strings.Join(ids, ".")
}

Unmarshal map[string]DynamoDBAttributeValue into a struct

I'm trying to set-up an AWS-lambda using aws-sdk-go that is triggered whenever a new user is added to a certain dynamodb table.
Everything is working just fine but I can't find a way to unmarshal a map map[string]DynamoDBAttributeValue like:
{
"name": {
"S" : "John"
},
"residence_address": {
"M": {
"address": {
"S": "some place"
}
}
}
}
To a given struct, for instance, a User struct. Here is shown an example of unsmarhaling a map[string]*dynamodb.AttributeValue into a given interface, but I can't find a way to do the same thing with map[string]DynamoDBAttributeValue even though these types seem to fit the same purposes.
map[string]DynamoDBAttributeValue is returned by a events.DynamoDBEvents from package github.com/aws/aws-lambda-go/events. This is my code:
package handler
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
func HandleDynamoDBRequest(ctx context.Context, e events.DynamoDBEvent) {
for _, record := range e.Records {
if record.EventName == "INSERT" {
// User Struct
var dynamoUser model.DynamoDBUser
// Of course this can't be done for incompatible types
_ := dynamodbattribute.UnmarshalMap(record.Change.NewImage, &dynamoUser)
}
}
}
Of course, I can marshal record.Change.NewImage to JSON and unmarshal it back to a given struct, but then, I would have to manually initialize dynamoUser attributes starting from the latter ones.
Or I could even write a function that parses map[string]DynamoDBAttributeValue to map[string]*dynamodb.AttributeValue like:
func getAttributeValueMapFromDynamoDBStreamRecord(e events.DynamoDBStreamRecord) map[string]*dynamodb.AttributeValue {
image := e.NewImage
m := make(map[string]*dynamodb.AttributeValue)
for k, v := range image {
if v.DataType() == events.DataTypeString {
s := v.String()
m[k] = &dynamodb.AttributeValue{
S : &s,
}
}
if v.DataType() == events.DataTypeBoolean {
b := v.Boolean()
m[k] = &dynamodb.AttributeValue{
BOOL : &b,
}
}
// . . .
if v.DataType() == events.DataTypeMap {
// ?
}
}
return m
}
And then simply use dynamodbattribute.UnmarshalMap, but on events.DataTypeMap it would be quite a tricky process.
Is there a way through which I can unmarshal a DynamoDB record coming from a events.DynamoDBEvent into a struct with a similar method shown for map[string]*dynamodb.AttributeValue?
I tried the function you provided, and I met some problems with events.DataTypeList, so I managed to write the following function that does the trick:
// UnmarshalStreamImage converts events.DynamoDBAttributeValue to struct
func UnmarshalStreamImage(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error {
dbAttrMap := make(map[string]*dynamodb.AttributeValue)
for k, v := range attribute {
var dbAttr dynamodb.AttributeValue
bytes, marshalErr := v.MarshalJSON(); if marshalErr != nil {
return marshalErr
}
json.Unmarshal(bytes, &dbAttr)
dbAttrMap[k] = &dbAttr
}
return dynamodbattribute.UnmarshalMap(dbAttrMap, out)
}
I was frustrated that the type of NewImage from the record wasn't map[string]*dynamodb.AttributeValue so I could use the dynamodbattribute package.
The JSON representation of events.DynamoDBAttributeValue seems to be the same as the JSON represenation of dynamodb.AttributeValue.
So I tried creating my own DynamoDBEvent type and changed the type of OldImage and NewImage, so it would be marshalled into map[string]*dynamodb.AttributeValue instead of map[string]events.DynamoDBAttributeValue
It is a little bit ugly but it works for me.
package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"fmt"
)
func main() {
lambda.Start(lambdaHandler)
}
// changed type of event from: events.DynamoDBEvent to DynamoDBEvent (see below)
func lambdaHandler(event DynamoDBEvent) error {
for _, record := range event.Records {
change := record.Change
newImage := change.NewImage // now of type: map[string]*dynamodb.AttributeValue
var item IdOnly
err := dynamodbattribute.UnmarshalMap(newImage, &item)
if err != nil {
return err
}
fmt.Println(item.Id)
}
return nil
}
type IdOnly struct {
Id string `json:"id"`
}
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"`
// changed to map[string]*dynamodb.AttributeValue
Keys map[string]*dynamodb.AttributeValue `json:"Keys,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
NewImage map[string]*dynamodb.AttributeValue `json:"NewImage,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
OldImage map[string]*dynamodb.AttributeValue `json:"OldImage,omitempty"`
SequenceNumber string `json:"SequenceNumber"`
SizeBytes int64 `json:"SizeBytes"`
StreamViewType string `json:"StreamViewType"`
}
I have found the same problem and the solution is to perform a simple conversion of types. This is possible because in the end the type received by lambda events events.DynamoDBAttributeValue and the type used by the SDK V2 of AWS DynamoDB types.AttributeValue are the same. Next I show you the conversion code.
package aws_lambda
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
func UnmarshalDynamoEventsMap(
record map[string]events.DynamoDBAttributeValue, out interface{}) error {
asTypesMap := DynamoDbEventsMapToTypesMap(record)
err := attributevalue.UnmarshalMap(asTypesMap, out)
if err != nil {
return err
}
return nil
}
func DynamoDbEventsMapToTypesMap(
record map[string]events.DynamoDBAttributeValue) map[string]types.AttributeValue {
resultMap := make(map[string]types.AttributeValue)
for key, rec := range record {
resultMap[key] = DynamoDbEventsToTypes(rec)
}
return resultMap
}
// DynamoDbEventsToTypes relates the dynamo event received by AWS Lambda with the data type that is
// used in the Amazon SDK V2 to deal with DynamoDB data.
// This function is necessary because Amazon does not provide any kind of solution to make this
// relationship between types of data.
func DynamoDbEventsToTypes(record events.DynamoDBAttributeValue) types.AttributeValue {
var val types.AttributeValue
switch record.DataType() {
case events.DataTypeBinary:
val = &types.AttributeValueMemberB{
Value: record.Binary(),
}
case events.DataTypeBinarySet:
val = &types.AttributeValueMemberBS{
Value: record.BinarySet(),
}
case events.DataTypeBoolean:
val = &types.AttributeValueMemberBOOL{
Value: record.Boolean(),
}
case events.DataTypeList:
var items []types.AttributeValue
for _, value := range record.List() {
items = append(items, DynamoDbEventsToTypes(value))
}
val = &types.AttributeValueMemberL{
Value: items,
}
case events.DataTypeMap:
items := make(map[string]types.AttributeValue)
for k, v := range record.Map() {
items[k] = DynamoDbEventsToTypes(v)
}
val = &types.AttributeValueMemberM{
Value: items,
}
case events.DataTypeNull:
val = nil
case events.DataTypeNumber:
val = &types.AttributeValueMemberN{
Value: record.Number(),
}
case events.DataTypeNumberSet:
val = &types.AttributeValueMemberNS{
Value: record.NumberSet(),
}
case events.DataTypeString:
val = &types.AttributeValueMemberS{
Value: record.String(),
}
case events.DataTypeStringSet:
val = &types.AttributeValueMemberSS{
Value: record.StringSet(),
}
}
return val
}
There is a package that allows conversion from events.DynamoDBAttributeValue to dynamodb.AttributeValue
https://pkg.go.dev/github.com/aereal/go-dynamodb-attribute-conversions/v2
From there one can unmarshal AttributeValue into struct
func Unmarshal(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error {
av := ddbconversions.AttributeValueMapFrom(attribute)
return attributevalue.UnmarshalMap(av, out)
}

Making code more generic

I have a program where many functionalities are similar across different structures, however, I end up writing these functions again and again, esp because the variable that are being dealt inside are of different structures.
I have written a sample code here.
In Go Playgroud
package main
import "fmt"
func (a *Match) Add(v Match) {
a.Runs += v.Runs
a.Points += v.Points
}
type Match struct {
Runs uint64
Points uint64
}
func (a *Activity) Add(v Activity) {
a.Walk += v.Walk
a.Jog += v.Jog
}
type Activity struct {
Walk uint64
Jog uint64
}
func GetDailyMatches() map[string]Match {
var dailyMatches map[string]Match
Match1, Match2 := Match{5, 10}, Match{1, 2}
dailyMatches = make(map[string]Match)
dailyMatches["01"] = Match1
dailyMatches["02"] = Match2
dailyMatches["03"] = Match1
dailyMatches["04"] = Match2
return dailyMatches
}
func GetDailyActivities() map[string]Activity {
var dailyActivities map[string]Activity
Activity1, Activity2 := Activity{5, 10}, Activity{1, 2}
dailyActivities = make(map[string]Activity)
dailyActivities["01"] = Activity1
dailyActivities["02"] = Activity2
dailyActivities["03"] = Activity1
dailyActivities["04"] = Activity2
return dailyActivities
}
func main() {
fmt.Println(CalculateMatchSummary("01", "03"))
fmt.Println(CalculateActivitySummary("02", "04"))
fmt.Println(CalculateMatchSummary("01", "03"))
fmt.Println(CalculateActivitySummary("02", "04"))
}
func CalculateMatchSummary(start, end string) (total Match) {
dailyMatches := GetDailyMatches()
for day, value := range dailyMatches {
if day < start {
continue
} else if day > end {
continue
} else {
total.Add(value)
}
}
return
}
func CalculateActivitySummary(start, end string) (total Activity) {
dailyActivities := GetDailyActivities()
for day, value := range dailyActivities {
if day < start {
continue
} else if day > end {
continue
} else {
total.Add(value)
}
}
return
}
If you notice, both Match and Activity has the same functions and same structures, except that internally they are of different structures.
Is there a easy way to make the code more generic (Go generics, which is not there in Go??) in Golang itself.
Go has a pretty package "reflect". You can not do genericity strictly speaking but you can get unification of code for the same behavior.
I've changed your playground a bit : https://play.golang.org/p/bfqZsFOgVQ
The main part :
func AddTwo(a, b interface{}) interface{} {
va := reflect.ValueOf(a)
vb := reflect.ValueOf(b)
res := reflect.New(reflect.TypeOf(a)).Elem()
if va.Kind() != reflect.Struct && vb.Kind() != reflect.Struct {
return nil
}
na, nb := va.NumField(), vb.NumField()
if na != nb {
return nil
}
for i := 0; i < na; i++ {
// additional verification needed here
fa := va.Field(i).Uint()
fb := vb.Field(i).Uint()
fr := fa + fb
res.Field(i).SetUint(fr)
}
return res.Interface()
}
I use reflect to check the fields of the struct I am given. If both are uint64, I can add them reflectively. If your structs contains many uint64, it can add them all !
Note that you must convert the resulting interface to the type of the struct given after calling this function. That is why this is not strictly generic, because the returning type is a interface, and not a Match or Activity.
EDIT: No need even to return a new struct. You can simply update the field of the "a" struct by calling .SetUint() method.

Generic variadic argument in Go?

I know that Go doesn't support templates or overloaded functions, but I'm wondering if there's any way to do some kind of generic programming for variadic functions anyway?
I have many functions such as these:
func (this Document) GetString(name string, defaults ...string) string {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(defaults) >= 1 {
return defaults[0]
} else {
return ""
}
}
return v.asString
}
func (this Document) GetInt(name string, defaults ...int) int {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(defaults) >= 1 {
return defaults[0]
} else {
return 0
}
}
return v.asInt
}
// etc. for many different types
Is there any way to do this without having so much redundant code?
The most of what you can achieve is usage of interface{} type, something like this:
func (this Document) Get(name string, defaults ...interface{}) interface{} {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(defaults) >= 1 {
return defaults[0]
} else {
return 0
}
}
return v
}
GetValueFromDb function should also be tweaked to return interface{} value and not some wrapper like now.
Then in the client code you can do the following:
value := document.Get("index", 1).(int) // Panics when the value is not int
or
value, ok := document.Get("index", 1).(int) // ok is false if the value is not int
This will yield some runtime overhead though. I'd better stick with separate functions and try to restructure the code somehow.
Here's a working example of how you could change your code.
package main
import (
"fmt"
)
type Document struct{
getSucceeds bool
}
func (d *Document) GetValueFromDb(name string) (interface{}, bool) {
return 1, d.getSucceeds
}
func (this Document) Get(name string, def ...int) interface{} {
v, ok := this.GetValueFromDb(name)
if !ok {
if len(def) >= 1 {
return def[0]
} else {
return 0
}
}
return v
}
func main() {
d1 := Document{true}
d2 := Document{false}
var int1, int2 int
int1 = d1.Get("foo", 2).(int)
int2 = d2.Get("foo", 2).(int)
fmt.Println(int1, int2)
}
Since you know what type you expect for the given name, you can write your Get method in a generic way, returning interface{}, and then assert the type at the call site. See the spec about type assertions.
There are different ways to emulate some aspects of generics in Go. There were lots of discussions on the mailing list. Often, there's a way to restructure code so it's less dependent on generics.
In the client code you can do like this :
res := GetValue("name", 1, 2, 3)
// or
// res := GetValue("name", "one", "two", "three")
if value, ok := res.(int); ok {
// process int return value
} else if value, ok := res.(string); ok {
// process string return value
}
// or
// res.(type) expression only work in switch statement
// and 'res' variable's type have to be interface type
switch value := res.(type) {
case int:
// process int return value
case string:
// process string return value
}

Resources