如果值相同,合并字典键

5

这是一个奇怪的问题,我猜它其实很简单。我正在为我家的远程播放器构建歌词 Web 应用程序。它目前生成了一个带有正在播放歌曲的播放器字典,例如:

{
    'bathroom': <Song: Blur - Song 2>,
    'bedroom1': <Song: Blur - Song 2>,
    'kitchen': <Song: Meat Loaf - I'd Do Anything for Love (But I Won't Do That)>,
}

有时候这些玩家中的子集是同步的。因此,像上面那样,它们显示相同的值。我想在界面中将它们分组。在构建字典时我可以更聪明一些,但假设我不会那么做,是否有一种很好的方法来通过值合并键?

从上面的内容中得到的期望输出类似于:

{
    'bathroom,bedroom1': <Song: Blur - Song 2>,
    'kitchen': <Song: Meat Loaf - I'd Do Anything for Love (But I Won't Do That)>,
}

然而,这破坏了我想要查找的方式(我想按名称指定,因此这是一个字典)...是否有更好的集合可以为每个值拥有多个键,并指示何时合并重复项(并向后引用它们的所有键)?
有一个很好的答案,将其转换为歌曲的键和玩家列表作为值。这很好,但有时我想知道在命名的播放器上播放哪首歌。这就是为什么我最初选择了字典。
有没有好的方法可以保留双向查找(除了保留两个集合)?

2
为什么不反转结构呢?既然歌曲是独一无二的,它们可以成为这里的键吗? - Cyrbil
1
准确的说 :) 低声望问题 - Cyrbil
抱歉,我误解了问题,所以我删除了回答并进行了更新 - 但现在已经恢复了。 - David Z
1
保留这两个集合有什么问题吗?每个字典都很小,具有不同的目的,这是一个非常简单的解决方案。 - TigerhawkT3
2个回答

7
from itertools import groupby

x = {
    'bathroom': 'a',
    'bedroom1': 'a',
    'kitchen': 'b'
}


{
  ','.join(i[0] for i in v): k
  for k,v in groupby(sorted(x.iteritems(), key=lambda p: p[1]), lambda p: p[1])
}

3
我认为这需要一个中间步骤,即按值对(键,值)的列表进行排序,否则不能保证相同的值在迭代中一起出现。 - David Z

2
当涉及到大量数据时,关系型数据库非常有用。一个有两列(key和value)的数据库,并且在key列上有一个索引,就像一个字典一样。但是您也可以在value列上建立索引以实现有效的反向查找。
在您的情况下,由于涉及的数据量很小,我建议您只需创建一个defaultdict,并添加(value, key)对即可。
reverse_lookup = defaultdict(list)
for k, v in now_playing.items():
    reverse_lookup[v].append(k)

然后你可以使用','.join()来生成复合键。由于这些复合键将用于显示,而不是查找,所以我会将原始字典和反向查找字典都保留在内存中,并在需要执行查找时使用其中一个。找到正在演奏给定歌曲的其他玩家(并且可能已同步)的任务涉及两个查找,一个正向和一个反向,但它们是哈希表查找,因此增加的成本很小。
经过一些思考,还有其他更“有趣”的方法可以实现这一点:您可能能够扭曲不相交集数据结构以满足您的需求。每个播放器都有一个节点,当前正在播放的每首歌曲都有一个节点。节点按歌曲分组为集合,其中一个集合包含该歌曲的节点以及当前播放该歌曲的任何播放器的节点。如果将每个集合的节点(歌曲加播放器)放入循环链接列表中,则只要整个数据结构得到适当维护,就可以从任何节点开始并遍历列表,以迭代播放该歌曲的歌曲和播放器列表。
当然,诀窍在于找到一种有效的方法来维护该整体数据结构,即更新循环列表以更改歌曲。如果玩家真正已同步,则每次整个组移动到下一个曲目时,只需用另一个歌曲节点替换一个歌曲节点即可。但是,我可以想象您正在构建的应用程序经常需要进行其他类型的查找,而不会从不相交集结构中获得任何好处。

是的,那可能行得通。我需要改变一些逻辑,但是从歌曲而不是玩家的角度思考问题,实际上也可以解决许多其他问题。 - Oli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接