Golang中的map可以使用多个键对应一个值。

huangapple go评论91阅读模式
英文:

Golang map with multiple keys per value

问题

考虑以下XML数据结构:

<MediaItems>
    <item url="media/somefolder/pic1.jpg" id="1">
        <groups>
            <group>1</group>
            <group>2</group>
        </groups>
    </item>
    <item url="media/somefolder/pic2.jpg" id="2">
        <groups>
            <group>3</group>
            <group>7</group>
        </groups>
    </item>
</MediaItems>

由于我的XML数据结构/文件可能扩展到10000个或者更多的媒体项元素,我需要能够在解析后的Go映射中访问单个项,就像我们使用map[key]type一样-但我需要能够使用url或id作为键,并且我无法弄清楚如何创建一个具有两个键指向相同值的映射。

从上面解析的XML数据结构中,我需要在Go中解析它并将其存储在类似于以下类型的映射中:

map[string]string]MediaItem

其中键应该是url和id,所以我可以通过myMap["1"]myMap["media/somefolder/pic1.jpg"]来获取id为1的项。两者都应返回相应的MediaItem

我无法理解如何实现这一点,或者也许有更好的方法来实现相同的功能?

英文:

Consider the following XML data structure:

&lt;MediaItems&gt;
    &lt;item url=&quot;media/somefolder/pic1.jpg&quot; id=&quot;1&quot;&gt;
        &lt;groups&gt;
            &lt;group&gt;1&lt;/group&gt;
            &lt;group&gt;2&lt;/group&gt;
        &lt;/groups&gt;
    &lt;/item&gt;
    &lt;item url=&quot;media/somefolder/pic2.jpg&quot; id=&quot;2&quot;&gt;
        &lt;groups&gt;
            &lt;group&gt;3&lt;/group&gt;
            &lt;group&gt;7&lt;/group&gt;
        &lt;/groups&gt;
    &lt;/item&gt;
&lt;/MediaItems&gt;

Since my XML data structure/file can potentially scale to 10000 or perhaps 100000+ media item elements, I need to be able to access the individual items, in the parsed Go map (or what structure to use here?), like we do with map[key]type - but I need to be able to use either the url or the id as a key, and I can't figure out how to create a map with 2 keys pointing to the same value.

From the parsed XML data structure above, I need to parse it in Go and store it in a type like:

map[string, string]MediaItem

Where the keys should be url and id, so I'd be able to get the item with id 1 doing myMap[&quot;1&quot;] or myMap[&quot;media/somefolder/pic1.jpg&quot;]. Both should return the corresponding MediaItem.)

I can't wrap my head around how to implement this, or maybe there a better way to achieve the same?

答案1

得分: 20

更好的解决方案是使用具有两个字段的结构体作为键:

type key struct {
    url string
    id  int
}

m := make(map[key]MediaItem)
m[key{url: "http://...", id: 2}] = MediaItem{}
英文:

Better solution would be to use struct with two field as a key:

type key struct {
    url string
    id  int
}

m := make(map[key]MediaItem)
m[key{url: &quot;http://...&quot;, id: 2}] = MediaItem{}

答案2

得分: 12

使用map类型,你可以使用2(3)种不同的解决方案:

使用2个map

最简单的方法是构建两个map,一个map的键是URL,另一个map的键是ID:

var byUrlMap map[string]*MediaItem
var byIdMap map[string]*MediaItem

请注意,为了避免重复值,应该存储指针而不是结构体。

如果你需要通过ID获取MediaItem

mi := byIdMap[id]

同样地,通过URL获取:

mi2 := byUrlMap

使用键前缀

另一种选择是给实际的键值添加前缀,但这样效率不高,不过结果是你只需要一个map。

例如,你可以给URL键添加前缀"url:",给ID键添加前缀"id:",当然,对于URL和ID键,你可以存储相同的指针值,例如:

var miMap = make(map[string]*MediaItem)

mi := &MediaItem{}
miMap["url:http://something"] = mi
miMap["id:1"] = mi

获取元素的方法:

mi2 := miMap["id:" + id]   // 通过ID
mi3 := miMap["url:" + url] // 通过URL

使用原始键

这与"使用键前缀"类似:如果你可以保证URL和ID永远不会相同(也就是说,你永远不会有一个ID与另一个项的URL相同,反之亦然),你可以简单地使用这两个键而不添加前缀,并将相同的值(指针)设置给它们。

键前缀的目的是确保最终的URL键永远不会与最终的ID键相同(通过为这两种键使用不同的前缀来实现)。但是,如果这个条件自然成立(例如,数字ID的string值永远不会是有效的URL),我们实际上不需要前缀:

var miMap = make(map[string]*MediaItem)

mi := &MediaItem{}
miMap["http://something"] = mi
miMap["1"] = mi

获取元素的方法:

mi2 := miMap[id]  // 通过ID
mi3 := miMap
// 通过URL
英文:

Staying with the map type, you can use 2 (3) different solutions:

With 2 maps

Easiest would be to build 2 maps, 1 where the keys are the urls, and 1 where the keys are the ids:

var byUrlMap map[string]*MediaItem
var byIdMap map[string]*MediaItem

Note that you should store pointers instead of structs for example to avoid duplicating the values.

If you need a MediaItem by id:

mi := byIdMap[id]

Similarly by url:

mi2 := byUrlMap

With key prefixes

Another option can be to prefix the actual key values, but this is not so efficient, but as a result, you'll have only one map.

For example you could prefix URL keys with &quot;url:&quot; and ids with &quot;id:&quot; and store the same pointer value of course for both the url and id keys, for example:

var miMap = make(map[string]*MediaItem)

mi := &amp;MediaItem{}
miMap[&quot;url:http://something&quot;] = mi
miMap[&quot;id:1&quot;] = mi

And getting an element:

mi2 := miMap[&quot;id:&quot; + id]   // By id
mi3 := miMap[&quot;url:&quot; + url] // By url

Using keys "as-is"

This is something similar to "With key prefixes": if you have guarantee that the URLs and ids will never be the same (meaning you will never have an id that is the same as another item's url and vice versa), you can simply use both keys without prefixes, and set the same value (pointer) to them.

The purpose of key prefixes was to make sure a final url key will never be the same as a final id key (achieved by using different prefixes for these 2 types of keys). But if this condition is naturally true (e.g. the string value of a numeric id will never be a valid url), we don't really need the prefixes:

var miMap = make(map[string]*MediaItem)

mi := &amp;MediaItem{}
miMap[&quot;http://something&quot;] = mi
miMap[&quot;1&quot;] = mi

And getting an element:

mi2 := miMap[id]  // By id
mi3 := miMap
// By url

huangapple
  • 本文由 发表于 2015年5月29日 20:44:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/30529970.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定