【问题标题】:Did I reinvent the wheel with this deduplicating function?我是否使用这种重复数据删除功能重新发明了轮子?
【发布时间】:2016-10-03 12:04:32
【问题描述】:

我一直在寻找类似 @​​987654323@ 的方法来删除列表中的重复数据,但原始列表中的项目不可散列(它们是 dicts)。

我花了一段时间寻找合适的东西,最后我写了这个小函数:

def deduplicate_list(lst, key):
    output = []
    keys = []
    for i in lst:
        if not i[key] in keys:
            output.append(i)
            keys.append(i[key])

    return output

如果key 被正确给出并且是string,这个函数就可以很好地完成它的工作。不用说,如果我了解允许相同功能的内置或标准库模块,我会很乐意放弃我的小例程,转而选择更标准和更强大的选择。

你知道这样的实现吗?

--注意

以下单行found from this answer

[dict(t) for t in set([tuple(d.items()) for d in l])]

虽然很聪明,但不会工作,因为我必须使用嵌套的 dicts 的项目。

-- 例子

为清楚起见,下面是使用此类例程的示例:

with_duplicates = [
    {
        "type": "users",
        "attributes": {
            "first-name": "John",
            "email": "john.smith@gmail.com",
            "last-name": "Smith",
            "handle": "jsmith"
        },
        "id": "1234"
    },
    {
        "type": "users",
        "attributes": {
            "first-name": "John",
            "email": "john.smith@gmail.com",
            "last-name": "Smith",
            "handle": "jsmith"
        },
        "id": "1234"
    }
]

without_duplicates = deduplicate_list(with_duplicates, key='id')

【问题讨论】:

  • 您能否在您的列表中提供deduplicate_list 的电话示例? (我看不清楚它是做什么的):)
  • 你不应该传递一个键列表吗?
  • 查看this 的答案 - 它可能会帮助您散列列表中的元素
  • @AlexisClarembeau 完成
  • dict([(x[key],x) for x in with_duplicates]).values()

标签: python python-3.x duplicates


【解决方案1】:

这个answer 将有助于解决一个更通用的问题 - 不是通过单个属性(在您的情况下为id)查找唯一元素,但如果 any 嵌套属性不同

以下代码将返回唯一元素的索引列表

import copy

def make_hash(o):

  """
  Makes a hash from a dictionary, list, tuple or set to any level, that contains
  only other hashable types (including any lists, tuples, sets, and
  dictionaries).
  """

  if isinstance(o, (set, tuple, list)):

    return tuple([make_hash(e) for e in o])    

  elif not isinstance(o, dict):

    return hash(o)

  new_o = copy.deepcopy(o)
  for k, v in new_o.items():
    new_o[k] = make_hash(v)

  return hash(tuple(frozenset(sorted(new_o.items()))))

l = [
    {
        "type": "users",
        "attributes": {
            "first-name": "John",
            "email": "john.smith@gmail.com",
            "last-name": "Smith",
            "handle": "jsmith"
        },
        "id": "1234"
    },
    {
        "type": "users",
        "attributes": {
            "first-name": "AAA",
            "email": "aaa.aaah@gmail.com",
            "last-name": "XXX",
            "handle": "jsmith"
        },
        "id": "1234"
    },
    {
        "type": "users",
        "attributes": {
            "first-name": "John",
            "email": "john.smith@gmail.com",
            "last-name": "Smith",
            "handle": "jsmith"
        },
        "id": "1234"
    },
]

# get indicies of unique elements
In [254]: list({make_hash(x):i for i,x in enumerate(l)}.values())
Out[254]: [1, 2]

【讨论】:

  • 不要使用哈希来确定两个对象是否相同。你总是有哈希冲突的风险。
  • @Rob,set 实现不是使用哈希(此函数的 C 或 Cython 实现)来生成“虚拟”密钥吗?我想使用set(lst) 是一种常见的做法 - 用于重复数据删除列表...
  • set 确实使用了哈希,但只是为了加快实现速度。如果两个对象具有相同的哈希值但不同的值,它们在set 中仍然是分开的,但在此实现中不是。
【解决方案2】:

您可以尝试基于您在问题中提供的答案链接的简短版本:

key = "id"
deduplicated = [val for ind, val in enumerate(l)
                if val[key] not in [tmp[key] for tmp in l[ind + 1:]]]
print(deduplicated)

注意,这将采用重复项的最后一个元素

【讨论】:

    【解决方案3】:

    对于key 的每个不同值,您只选择列表中的第一个dictitertools.groupby 是一个内置工具,可以为您做到这一点 - 按 key 排序和分组,并且只取每个组中的第一个:

    from itertools import groupby
    
    def deduplicate(lst, key):
        fnc = lambda d: d.get(key)  # more robust than d[key]
        return [next(g) for k, g in groupby(sorted(lst, key=fnc), key=fnc)]
    

    【讨论】:

    • 不,它给出:TypeError: unorderable types: dict() < dict()
    • 嗯 .. 它对我有用,并且是您示例中的确切列表。 Python 2.7.11 ubuntu 14.04,我明白了。所以可能不在 Python 3.x 中。
    • 我使用的是 Python 3.5.1。这是sorted(l) - 它给了我 Python 3 上的错误
    • 你也无法比较字典中某个键的值
    • @DomTomCat 是的,我一直在寻找更通用的解决方案。使用给定的密钥,它实际上应该变得更加健壮。更新了!
    【解决方案4】:

    在您的示例中,键返回的值是可散列的。如果总是这样,那么使用这个:

    def deduplicate(lst, key):
        return list({item[key]: item for item in lst}.values())
    

    如果有重复,只保留最后一个匹配的重复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-02
      • 1970-01-01
      • 2013-05-03
      • 2020-06-07
      • 1970-01-01
      • 2022-01-10
      • 1970-01-01
      相关资源
      最近更新 更多