从映射列表中提取唯一项
这里有一个有趣的问题,想要找到最符合Python风格的解决方案。假设我有一个映射列表 {'id': id, 'url': url}
。这个列表中有些 id
是重复的,我想创建一个新的列表,把所有重复的 id
去掉。我想出了下面这个函数:
def unique_mapping(map):
d = {}
for res in map:
d[res['id']] = res['url']
return [{'id': id, 'url': d[id]} for id in d]
我想这个方法还算高效。但是有没有更“Pythonic”的方式呢?或者说,有没有更高效的方法?
3 个回答
1
我觉得这个可以更简单一些。字典不允许有重复的键。你可以把你的映射列表变成一个映射的字典。这样就能去掉重复的部分。
>>> someListOfDicts= [
{'url': 'http://a', 'id': 'a'},
{'url': 'http://b', 'id': 'b'},
{'url': 'http://c', 'id': 'a'}]
>>> dict( [(x['id'],x) for x in someListOfDicts ] ).values()
[{'url': 'http://c', 'id': 'a'}, {'url': 'http://b', 'id': 'b'}]
2
这里有几个可以改进的地方。
你现在做了两个循环,一个是遍历原始字典,另一个是遍历结果字典。其实可以一步到位,直接在一个步骤中构建结果。
你可以使用生成器,这样就不用一开始就把整个列表都构建出来。如果需要完整的列表,可以用list(unique_mapping(items))来转换。
在检查重复项时,其实不需要存储值,可以用集合来代替。
你为每个元素都重新创建了一个字典,而不是直接返回原来的字典。这样做可能是有必要的(比如你在修改它们,不想动原来的),但如果不是的话,直接使用已经创建好的字典会更高效。
下面是一个实现示例:
def unique_mapping(items):
s = set()
for res in items:
if res['id'] not in s:
yield res
s.add(res['id'])
4
你的例子可以稍微改写一下,用生成器表达式来构建第一个字典,这样就不需要再构建另一个映射了。可以直接重复使用旧的:
def unique_mapping(mappings):
return dict((m['id'], m) for m in mappings).values()
虽然这变成了一行代码,但我觉得还是挺容易理解的。
在使用你原来的解决方案和我这个方案时,有两点需要注意:
- 返回的项目顺序可能和最开始的不一样
- 后面的条目会覆盖掉之前相同ID的条目
如果你不介意这些问题,我建议使用上面的解决方案。如果你在意顺序,那么这个函数会保留顺序,并优先处理第一次遇到的ID:
def unique_mapping(mappings):
addedIds = set()
for m in mappings:
mId = m['id']
if mId not in addedIds:
addedIds.add(mId)
yield m
如果你需要一个列表而不是生成器,可能需要用 list(unique_mappings(mappings))
来调用它。