从映射列表中提取唯一项

2 投票
3 回答
425 浏览
提问于 2025-04-11 09:25

这里有一个有趣的问题,想要找到最符合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)) 来调用它。

撰写回答