【问题标题】:Best way to delete duplicate values in a list of dictionaries?删除字典列表中重复值的最佳方法?
【发布时间】:2017-04-08 19:21:26
【问题描述】:

我有一个字典列表,这些字典还有另一个嵌套字典。这是一个例子:

reports = [
            {'00T2A00003mDvq9': {'subject': 'dupe1', 'due_date': '4/5/2017'}}
            {'00T2A00003mDvq8': {'subject': 'dupe2', 'due_date': '4/7/2017'}}
            {'00T2A00003mDvq7': {'subject': 'dupe1', 'due_date': '4/3/2017'}}
          ]

所以列表中的每个 dict 都有一个唯一的 id 和与之关联的值。

我需要一种方法来遍历这些字典,如果它们中的任何一个在 'subject' 字段中完全匹配,那么我想删除/删除具有 latest 日期的整个字典。

因此,使用上面的示例,在遍历列表和重复数据删除之后,我需要结果看起来像这样。

reports = [
            {'00T2A00003mDvq8': {'subject': 'dupe2', 'due_date': '4/7/2017'}}
            {'00T2A00003mDvq9': {'subject': 'dupe1', 'due_date': '4/3/2017'}}
          ]

它会删除'dupe1' 的第一个实例,因为它是较晚的日期。

【问题讨论】:

  • 你尝试过什么,它到底有什么问题?
  • 我已经看到了几个迭代字典列表的例子,但没有一个像我上面那样使用嵌套字典。
  • 这不是我问的; SO 不是代码编写服务,您需要自己为实际实现付出一些努力。此外,请不要还原合法的编辑;一次问一个问题(最好在阅读How to Ask之后)。
  • 如果我知道解决方案或知道如何去做,我就不会在这里发帖。来自 SO 关于我们的页面:“在您的帮助下,我们正在共同努力建立一个包含有关编程的每个问题的详细答案的库。”。我不是在寻找代码编写服务。我正在寻找我的问题的答案。我在这里提出了奖金问题,因为我猜如果它被排序,做我正在寻找的事情会更容易
  • 你能告诉我某个地方有与上述问题相关的答案吗?

标签: python list python-3.x sorting dictionary


【解决方案1】:

既然你完全被困住了,那就从这里开始吧。一个问题是,对于每个dict,密钥都是不同且未知的。看起来每个字典中只有一对,所以您可以获取items() 并获取第一个:

reports = [ 
    {'00T2A00003mDvq9': {'subject': 'dupe1', 'due_date': '4/5/2017'}},
    {'00T2A00003mDvq8': {'subject': 'dupe2', 'due_date': '4/7/2017'}},
    {'00T2A00003mDvq7': {'subject': 'dupe1', 'due_date': '4/3/2017'}}
]

def get_subject(some_dict):
    return list(some_dict.items())[0][1]['subject']

reports.sort(key=get_subject)
print(reports)
# [{'00T2A00003mDvq9': {'due_date': '4/5/2017', 'subject': 'dupe1'}}, {'00T2A00003mDvq7': {'due_date': '4/3/2017', 'subject': 'dupe1'}}, {'00T2A00003mDvq8': {'due_date': '4/7/2017', 'subject': 'dupe2'}}]

reports 现在按主题排序。然后您可以使用groupby 获取按主题分组的报告。

对于每个组,您可以再次使用sort,这次使用due_date。不过您必须小心,您不能按字母顺序对日期进行排序,您需要按此顺序提取 year,month,day 或使用 strptime 将字符串转换为 datetime 对象。

将结果按subject 分组并按due_date 排序后,只需获取每个组的第一个元素。完成!

【讨论】:

【解决方案2】:

问题变得更加困难,因为您不知道reports 中字典的键值(唯一ID)。由于每一项仅包含一项,因此您可以在 Python 3 中使用 next(iter(dict.values())) 来获取与其关联的单个嵌套字典——我在下面的代码中将其称为 checkout 以为其命名。

鉴于此,我将使用的方法是首先创建一个字典,按主题对 reports 中的元素进行分组,然后为您提供类似的内容(注意:我更改了样本reports 数据,因此第一个有多个重复的'subject'):

{
    'dupe1': [
        {'00T2A00003mDvq9': {'due_date': '4/5/2017', 'subject': 'dupe1'}},
        {'00T2A00003mDvq7': {'due_date': '4/3/2017', 'subject': 'dupe1'}},
        {'00T2A00003mDvq6': {'due_date': '4/6/2017', 'subject': 'dupe1'}}
    ],
    'dupe2': [
        {'00T2A00003mDvq8': {'due_date': '4/7/2017', 'subject': 'dupe2'}}
    ]
}

然后可以按日期对与每个主题相关联的报告列表进行排序(使用基于相同 next(iter(dict.values())) 技巧的 lambda),并且鉴于现在排序的内容,很容易更新列表并删除根据您的需要进行任何重复。

from time import strptime
from pprint import pprint

DATE_FMT = '%m/%d/%Y'
reports = [
    {'00T2A00003mDvq9': {'subject': 'dupe1', 'due_date': '4/5/2017'}},
    {'00T2A00003mDvq8': {'subject': 'dupe2', 'due_date': '4/7/2017'}},
    {'00T2A00003mDvq7': {'subject': 'dupe1', 'due_date': '4/3/2017'}},
    {'00T2A00003mDvq6': {'subject': 'dupe1', 'due_date': '4/6/2017'}},  # + a third duplicate
]

by_subject = {}
for report in reports:
    checkout = next(iter(report.values()))  # get single subdictionary in each dictionary
    by_subject.setdefault(checkout['subject'], []).append(report)

for records in by_subject.values():
    records.sort(key=lambda rpt: strptime(next(iter(rpt.values()))['due_date'], DATE_FMT))

# Update reports list in-place.
del reports[:]
for subject, records in by_subject.items():
    reports.append(records[0])  # only keep oldest (deletes all newer than first)

print('Deduped reports:')
pprint(reports)

输出:

Deduped reports:
[{'00T2A00003mDvq7': {'due_date': '4/3/2017', 'subject': 'dupe1'}},
 {'00T2A00003mDvq8': {'due_date': '4/7/2017', 'subject': 'dupe2'}}]

【讨论】:

  • 这几乎可以正常工作并且朝着正确的方向发展。一个问题是我可以有很多重复,这个只删除 1。此外,我不需要保留重复数据删除报告。希望保留原始列表减去重复项。
  • 您的问题是“我想删除/删除具有最新日期的 整个字典”(强调我的),这似乎表明只删除一个。无论如何,我已经根据您的反馈(我认为)更新了我的答案。
  • 我发布了我的最终解决方案。我认为你的没有完全工作,因为我使用的是 python3,但这几乎正是你的答案。谢谢!
  • 是的,错过了 Python 3 标签,抱歉。不过,修复起来很简单(请参阅更新的答案)......不客气。
【解决方案3】:

这是我采用的最终解决方案。基于@martineau 的回答,但我猜这只是因为我使用的是 Python3。

from time import strptime

DATE_FMT = '%m/%d/%Y'
reports = [
    {'00T2A00003mDvq9': {'subject': 'dupe1', 'due_date': '4/5/2017'}},
    {'00T2A00003mDvq8': {'subject': 'dupe2', 'due_date': '4/7/2017'}},
    {'00T2A00003mDvq7': {'subject': 'dupe1', 'due_date': '4/3/2017'}},
    {'00T2A00003mDvq6': {'subject': 'dupe1', 'due_date': '4/6/2017'}},  # + third duplicate
]

DATE_FMT = '%m/%d/%Y'

    by_subject = {}
    for report in reports:
        topic = list(report.values())[0]
        # assuming only one element in each dictionary
        by_subject.setdefault(topic['subject'], []).append(report)

    for records in by_subject.values():
        records.sort(key=lambda rec: strptime(list(rec.values())[0]['due_date'], DATE_FMT))

    reports = []

    for subject, records in by_subject.items():

        if len(records) > 1:
            while len(records) != 1:
                del records[-1]
        reports.extend(records)

【讨论】:

    猜你喜欢
    • 2018-03-13
    • 2012-07-15
    • 2017-11-07
    • 1970-01-01
    • 2015-11-02
    • 2015-08-25
    • 2022-11-27
    • 1970-01-01
    • 2020-08-03
    相关资源
    最近更新 更多