Jeeyoung Kim
Jeeyoung Kim

Reputation: 6078

Using variable-length array as a map key in golang

I can't find a good way to do this. I want to have a map from a list of sorted key-value pairs.

type Tag struct {
  key   string
  value string
}

type SortedTag []Tag // sorted list of tags.
map[SortedTags]T // cannot do.

I can solve this problem by joining all the key-value pairs with a delimiter, but I feel like this is inefficient and error prone in many ways. Converting back to the key-value pair is cumbersome because we need to split the input. moreover, if the key value pair can be anything, that means we have to escape it.

If it was python, I would've stored Tag as N-tuple of sorted 2-tupless.

If it was java, I would've created a composite object with Map<String,String> with equals() checking against the other hash map, hashCode() returning the xor of all the hashes of the map (xor since it is commutative thus we can iterate the map in any order to compute this value).

In go, I can't think of any other good way.

Upvotes: 4

Views: 3841

Answers (2)

peterSO
peterSO

Reputation: 166529

For example,

package main

import "fmt"

type Tag struct {
    Key   string
    Value string
}

type Tags []Tag

type TagsValue struct {
    // some type used as Tags value
}

type TagsMapValue struct {
    Tags
    TagsValue
}

type TagsMapKey string

type TagsMap map[TagsMapKey]TagsMapValue

func NewTagsMapKey(tags Tags) TagsMapKey {
    b := []byte{}
    for _, tag := range tags {
        b = append(b, tag.Key...)
        b = append(b, tag.Value...)
    }
    return TagsMapKey(b[:len(b)])
}

func (m *TagsMap) AddElement(tags Tags, tagsValue TagsValue) {
    mapKey := NewTagsMapKey(tags)
    mapValue := TagsMapValue{Tags: make(Tags, 0, len(tags)), TagsValue: tagsValue}
    i := 0
    for _, tag := range tags {
        key := string(mapKey[i : i+len(tag.Key)])
        i += len(tag.Key)
        value := string(mapKey[i : i+len(tag.Value)])
        i += len(tag.Value)
        mapValue.Tags = append(mapValue.Tags, Tag{Key: key, Value: value})
    }
    (*m)[mapKey] = mapValue
    return
}

func main() {
    m := make(TagsMap)
    sortedTags := Tags{
        {Key: "key1", Value: "value1"},
        {Key: "key7", Value: "value7"},
        {Key: "key7", Value: "value49"},
        {Key: "key42", Value: "value42"},
    }
    m.AddElement(sortedTags, TagsValue{})
    for k, v := range m {
        fmt.Println("Tags Key:", k)
        fmt.Println("   Tags:      ", v.Tags)
        fmt.Println("   Tags Value:", v.TagsValue)
    }
}

Output:

Tags Key: key1value1key7value7key7value49key42value42
   Tags:       [{key1 value1} {key7 value7} {key7 value49} {key42 value42}]
   Tags Value: {}

If you are simply trying to test for Tags set membership,

package main

import "fmt"

type Tag struct {
    Key   string
    Value string
}

type Tags []Tag

type TagsSetKey string

type TagsSet map[TagsSetKey]Tags

func NewTagsSetKey(tags Tags) TagsSetKey {
    b := []byte{}
    for _, tag := range tags {
        b = append(b, tag.Key...)
        b = append(b, tag.Value...)
    }
    return TagsSetKey(b[:len(b)])
}

func (m *TagsSet) AddElement(tags Tags) {
    setKey := NewTagsSetKey(tags)
    setValue := make(Tags, 0, len(tags))
    i := 0
    for _, tag := range tags {
        key := string(setKey[i : i+len(tag.Key)])
        i += len(tag.Key)
        value := string(setKey[i : i+len(tag.Value)])
        i += len(tag.Value)
        setValue = append(setValue, Tag{Key: key, Value: value})
    }
    (*m)[setKey] = setValue
    return
}

func (m *TagsSet) IsMember(tags Tags) bool {
    return (*m)[NewTagsSetKey(tags)] != nil
}

func main() {
    m := make(TagsSet)
    sortedTags := Tags{
        {Key: "key1", Value: "value1"},
        {Key: "key7", Value: "value7"},
        {Key: "key7", Value: "value49"},
        {Key: "key42", Value: "value42"},
    }
    m.AddElement(sortedTags)
    for k, v := range m {
        fmt.Println("Tags Key:", k)
        fmt.Println("   Tags: ", v)
    }
    // In set
    fmt.Println(m.IsMember(sortedTags))
    // Not in set
    sortedTags[0].Key = "key0"
    fmt.Println(m.IsMember(sortedTags))
}

Output:

Tags Key: key1value1key7value7key7value49key42value42
   Tags:  [{key1 value1} {key7 value7} {key7 value49} {key42 value42}]
true
false

Upvotes: 3

VonC
VonC

Reputation: 1323045

if you are after (sorted) tuples, you can check out kmanley/golang-tuple

It does have examples of sorting tuples.

This is different from deckarep/golang-set, which can also be helpful for managing those Tag.

Upvotes: 0

Related Questions