查找与另一个字典中的键,值对匹配的字典的Python方法

时间:2020-04-28 13:43:49

标签: python python-3.x list dictionary

我正在尝试找到一种方法来将一个字典的键,值对与另一个字典进行匹配。第一个字典record是具有不变数量的键的记录(虽然每个键的值当然可以更改),但是第二个字典potential_outputs是用户定义的,具有可变的键和值。用户从record中选择要分配的键,给它们分配一个值,然后分配找到匹配项时使用的输出值。

示例:

record = [
    {"Name": "John Smith", "Class": "c1", "Plan": "p1",},
    {"Name": "Jane Doe", "Class": "c2", "Plan": "p2",},
]
potential_outputs = [
    {"Class": "c1", "Plan": "p1", "Output": "o11"},
    {"Class": "c1", "Plan": "p2", "Output": "o12"},
    {"Class": "c2", "Plan": "p1", "Output": "o21"},
    {"Class": "c2", "Plan": "p2", "Output": "o22"},
]

程序需要能够遍历record列表中的每个字典,确定potential_outputs中的哪个字典与键,值对匹配,然后从匹配的{ {1}}字典。

预期输出类似于:

potential_outputs

我还想指出,我不致力于使用字典来解决此问题。

谢谢!

7 个答案:

答案 0 :(得分:2)

您可以使用(Class, Plan)元组键对输出进行分组,然后使用列表推导输出找到的输出字典。

使用输出查找字典进行O(1)查找,可以使解决方案成为O(N + M),而不是O(N * M),其中N是{{1}中词典的数目},而recordM中词典的数量。

potential_outputs

输出:

record = [
    {"Name": "John Smith", "Class": "c1", "Plan": "p1",},
    {"Name": "Jane Doe", "Class": "c2", "Plan": "p2",},
]

potential_outputs = [
    {"Class": "c1", "Plan": "p1", "Output": "o11"},
    {"Class": "c1", "Plan": "p2", "Output": "o12"},
    {"Class": "c2", "Plan": "p1", "Output": "o21"},
    {"Class": "c2", "Plan": "p2", "Output": "o22"},
]

outputs = {(output["Class"], output["Plan"]): output["Output"] for output in potential_outputs}

result = [{"Name": r["Name"], "Output": outputs[r["Class"], r["Plan"]]} for r in record]

print(result)

答案 1 :(得分:1)

为避免嵌套循环和M * N复杂性,您可以预处理record

from collections import defaultdict

rec = defaultdict(lambda: defaultdict(list))
for r in record:
    rec[r['Class']][r['Plan']].append(r['Name'])

在遍历potential_outputs

之前
result = [{"Name": name, "Output": po["Output"]} 
          for po in potential_outputs 
          for name in rec[po['Class']][po['Plan']]]
result
# [{'Name': 'John Smith', 'Output': 'o11'}, {'Name': 'Jane Doe', 'Output': 'o22'}]

答案 2 :(得分:1)

可以做到这一点,并且通过创建要用作索引的第三个字典比线性性能更好。 索引字典上的“关键字”应该是关键字/值对的集合,它们可以是所需输出记录的有效标识符。看起来如果您使用包含元组的FrosenSets生成此索引-类似于:


def make_index(data):
    result_index = {}
    for row in data:
        work_row = row.copy()
        work_row.pop("Output")
        while work_row:
            key = frozenset((key, value) for key, value in work_row.items())
            result_index.setdefault(key, []).append(row)
            work_row.pop(next(iter(work_row))) 
    return result_index


def search(index, row_key):
    row_key = row_key.copy()
    row_key.pop("Name", None)
    key = frozenset((key, value) for key, value in row_key.items())
    return index[key]

如果“ potential_outputs”具有除“名称”之外的所有键,这将起作用:

In [35]: search(index, record[0])                                                                                                                    
Out[35]: [{'Class': 'c1', 'Plan': 'p1', 'Output': 'o11'}]

In [36]: index = make_index(potential_outputs)                                                                                                       

In [37]: search(index, record[0])                                                                                                                    
Out[37]: [{'Class': 'c1', 'Plan': 'p1', 'Output': 'o11'}]

如果您希望mtches的匹配密钥少于 只是剥离名称,相同的索引有效,但“搜索” 代码必须更改。然后我们必须确切地知道 要查询的匹配项是什么?如果是“ class”并且 “计划”匹配不同的记录,是否都应退回?还是没有? 您可能会在itertools中找到一些东西来生成 给定记录行,您要搜索的所有键。

无论如何,这段代码已经适合 如果一切都匹配,则可以恢复多个结果:


In [39]: search(index, {"Plan": "p2"})                                                                                                               
Out[39]: 
[{'Class': 'c1', 'Plan': 'p2', 'Output': 'o12'},
 {'Class': 'c2', 'Plan': 'p2', 'Output': 'o22'}]

答案 3 :(得分:0)

这是使用pandas处理该问题的一种非常简单的方法:

import pandas as pd

# Read your list of dicts into DataFrames.
dfr = pd.DataFrame(record)
dfp = pd.DataFrame(potential_outputs)

# Merge the two DataFrames on `Class` and `Plan` and return the result.
result = pd.merge(dfr, 
                  dfp, 
                  how='inner', 
                  on=['Class', 'Plan']).drop(['Class', 'Plan'], axis=1)

输出1:

作为数据框:

    Name    Output
0   John Smith  o11
1   Jane Doe    o22

输出2:

作为列表:

result2 = [i for i in result.T.to_dict().values()]

[{'Name': 'John Smith', 'Output': 'o11'}, {'Name': 'Jane Doe', 'Output': 'o22'}]

答案 4 :(得分:0)

如果您要使用以下形式使potential_outputs成为字典 {("c1","p1"): "o11"},您可以这样做:

result = []
for a in record:
    if (a["Class"], a["Plan"]) in potential_outputs:
         result.append({"Name": a["Name"], "Output": potential_outputs[(a["Class"], a["Plan"])]})

这可能不是最好的方法,但将是纯Python的方法。

答案 5 :(得分:0)

如果您对单线飞机感兴趣

result = [{"Name": r["Name"], "Output": o["Output"]} for r in record for o in potential_outputs if r["Class"] == o["Class"] and r["Plan"] == o["Plan"]]

答案 6 :(得分:0)

您可以将potential_outputs重组为字典:

potential_output_dict = {
    f"{o['Class']}_{o['Plan']}": o['Output'] for o in potential_outputs
}

output = []
for r in record:
    plan_key = f"{r['Class']}_{r['Plan']}"
    plan = potential_output_dict.get(plan_key)
    if not plan:
        continue

    output.append({
        "Name": r['Name'],
        "Plan": plan,
     })

print(output)

通过这种方式,您使用get()比遍历字典列表要好一些。

(代码未经测试)

相关问题