如何在Python中将一个映射列表转换为一组映射?

3
在Python中,我有以下地图列表:
[{'CN': 'SC',
  'LB': 'g1k',
  'SM': 'HG1'},
 {'CN': 'SC',
  'LB': 'g2k',
  'SM': 'HG2'},
 {'CN': 'SC',
  'LB': 'g3k',
  'SM': 'HG3'}]

我希望您能提供类似于这样的地图:
{ 'CN' : 'SC',
  'LB' : ['g1k', 'g2k', 'g3k'],
  'SM' : ['HG1', 'HG2', 'HG3']
}

最符合Python风格的做法是什么?谢谢!

另外,我打算将新的数据结构转换为JSON格式以便在网页中显示。


7
请注意,您所期望的输出本身有些不符合Python的规范:除非有充分的理由,否则您不应该在字典中将一个值设为字符串,而其他值设为字符串列表。特别是在这里更是如此,因为字符串是可迭代的,因此针对“LB”和“SM”的值进行迭代是可以的,但在“SC”的情况下,它会迭代字符“S”和“C”,而len()会返回2而不是1,这可能不是预期的结果。 - undefined
我同意我的输出看起来可能不符合Python风格,但是我希望进一步将这个地图作为JSON发送到网页上。而且我不想多次显示像'CN':'SC'这样的内容。另外,我希望按照类别对它们进行分组。 - undefined
这就是为什么所有的答案都使用集合(sets)而不是列表(lists) - undefined
是的,观察得很好 - 我已经纠正了问题的标题。 - undefined
@TheodrosZelleke:我认为,为了序列化的目的,OP无论如何都必须将其转换回列表--我认为JSON没有固定的类型。 - undefined
在阅读关于JSON的评论后,我已经将转换回列表的代码添加到我的答案中。 - undefined
4个回答

5
>>> list_of_map = [{'CN': 'SC',
...   'LB': 'g1k',
...   'SM': 'HG1'},
...  {'CN': 'SC',
...   'LB': 'g2k',
...   'SM': 'HG2'},
...  {'CN': 'SC',
...   'LB': 'g3k',
...   'SM': 'HG3'}]
>>> from collections import defaultdict
>>> d = defaultdict(set)
>>> for map in list_of_map:
...     for k,v in map.items():
...         d[k].add(v)
... 
>>> d
defaultdict(<type 'set'>, {'LB': set(['g3k', 'g1k', 'g2k']), 'CN': set(['SC']), 'SM': set(['HG2', 'HG3', 'HG1'])})

这不完全是您想要的数据结构,但它非常接近,如果您真的需要,很容易进行更改。(我没有在这里展示它,因为我认为使用 set 而不是 list 更好)

回到列表:

>>> back_to_lists = {k:list(v) for k,v in d.items()}
>>> back_to_lists
{'LB': ['g3k', 'g1k', 'g2k'], 'CN': ['SC'], 'SM': ['HG2', 'HG3', 'HG1']}

甚至可以这样做:
>>> back_to_lists = {k:sorted(v) for k,v in d.items()}
>>> back_to_lists
{'LB': ['g1k', 'g2k', 'g3k'], 'CN': ['SC'], 'SM': ['HG1', 'HG2', 'HG3']}

愚蠢的问题:尽管这可能是唯一的方法,但是将所有内容复制到集合,然后再从集合复制回列表,这样做不是有点低效吗?(如果我拥有大型集合/列表) - undefined
1
@Clara -- 从内存效率来看,这种方法可能有些低效,但从计算角度来看,它可能是最快的方式。将所有内容保持为列表会降低性能,因为检查对象是否已经在列表中需要 O(N) 的时间(其中 N 是列表的长度)。相比之下,使用集合只需要 O(1) 的时间。将内容转换回列表也非常快速和容易 —— 这只是一个简单的迭代,其时间复杂度为 O(N),但只需执行一次即可。 - undefined
啊,好点!忘记了在列表中插入之前的搜索带来的开销。谢谢! - undefined

1
In [21]: nk=[{'CN': 'SC', 'LB': 'g1k', 'SM': 'HG1'},
    ...:  {'CN': 'SC', 'LB': 'g2k', 'SM': 'HG2'},
    ...:  {'CN': 'SC', 'LB': 'g3k', 'SM': 'HG3'}]

In [22]: result={}

In [23]: for x in nk:
    ...:     for k in x:
    ...:         result.setdefault(k,set()).add(x[k])
    ...:         

In [24]: result
Out[24]: 
{'CN': set(['SC']),
 'LB': set(['g3k', 'g1k', 'g2k']),
 'SM': set(['HG2', 'HG3', 'HG1'])}

or

In [60]: nk=[{'CN': 'SC', 'LB': 'g1k', 'SM': 'HG1'},
    ...:  {'CN': 'SC', 'LB': 'g2k', 'SM': 'HG2'},
    ...:  {'CN': 'SC', 'LB': 'g3k', 'SM': 'HG3'}]

In [61]: {z[0][0]:set(z[1]) for z in [zip(*x) for x in zip(*[y.items() for y in [x for x in nk]])]}
Out[61]: 
{'CN': set(['SC']),
 'LB': set(['g3k', 'g1k', 'g2k']),
 'SM': set(['HG2', 'HG3', 'HG1'])}

1
如果新字典中值的顺序不重要,您可以这样做:
In [1]: maps = [{'CN': 'SC',
   ...:   'LB': 'g1k',
   ...:   'SM': 'HG1'},
   ...:  {'CN': 'SC',
   ...:   'LB': 'g2k',
   ...:   'SM': 'HG2'},
   ...:  {'CN': 'SC',
   ...:   'LB': 'g3k',
   ...:  

In [2]: from itertools import chain

In [3]: {k: set(m[k] for m in maps if k in m) for k in chain.from_iterable(maps)}
Out[3]: 
{'CN': set(['SC']),
 'LB': set(['g2k', 'g1k', 'g3k']),
 'SM': set(['HG3', 'HG2', 'HG1'])}

作为一个附注,chain(*maps) 可以替换为 chain.from_iterable(maps),这样可以避免将 maps 转换成元组,并使其保持懒惰状态。 - undefined
如果顺序很重要,并且键始终相同(诚然,这是一个相当严格的要求),我考虑使用d2 = {k: [m[k] for m in list_of_map] for k in list_of_map[0]}或类似的方法。 - undefined
@mgilson 谢谢你关于chain的建议,不过你第一条评论让我有点困惑。你是指我的答案重复了你的吗? - undefined
@LevLevitsky -- 不好意思...我是说我很难理解它 -- 别介意,我总是在处理嵌套推导时感到困难。 - undefined
@mgilson 啊,好的。那么这次对我来说使用一个似乎很自然:这是我思考的顺序。 - undefined

0
我会选择不包装、不聪明、循环的选项。。
其中 d 是你的字典列表,md 是一个空字典。
for d in dicts:
 for k in d:
  if k in md:
   md[k].add(d[k])
  else:
   md[k]=set([d[k]])

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