【问题标题】:Pythonic way to find a dictionary that matches key, value pairs in another dictionary查找与另一个字典中的键、值对匹配的字典的 Pythonic 方法
【发布时间】:2020-04-28 13:43:49
【问题描述】:

我正在尝试找到一种方法来匹配一个字典的键值对和另一个字典。第一个字典 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 中的哪个字典匹配键、值对,然后从匹配的potential_outputs 字典中返回“输出”。

预期的输出将类似于以下内容:

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

我还想指出,我并没有承诺使用字典来解决这个问题。

谢谢!

【问题讨论】:

    标签: python python-3.x list dictionary


    【解决方案1】:

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

    使用O(1) 查找的输出查找字典允许解决方案为O(N + M),而不是O(N * M),其中Nrecord 中的字典数,Mpotential_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)
    

    输出:

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

    【讨论】:

      【解决方案2】:

      为避免嵌套循环和 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'}]
      

      【讨论】:

        【解决方案3】:

        通过创建第三个字典用作索引,可以做到这一点,并且比线性性能更好。 索引字典上的“键”应该是一组键/值对,它们可以是所需输出记录的有效标识符。看起来如果您使用包含元组的 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" 具有除 "Name" 之外的所有键,则此方法有效:

        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 出现的匹配键少于 只是剥离名称,相同的索引有效,但“搜索” 代码必须更改。然后我们必须确切地知道 相应查询所需的匹配项是什么。如果“类”和 “计划”匹配不同的记录,都应该返回吗?还是没有? 您可能会在 itertools 中找到要生成的内容 给定记录中的一行,您要搜索的所有键。

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

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

        【讨论】:

          【解决方案4】:

          如果你想让 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】:

            这是使用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'}]
            

            【讨论】:

              【解决方案6】:

              如果你对单线感兴趣

              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"]]
              

              【讨论】:

                【解决方案7】:

                您可以将 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(),这比多次遍历字典列表要好一些。

                (代码未测试)

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-12-23
                  • 1970-01-01
                  • 2017-08-05
                  • 1970-01-01
                  相关资源
                  最近更新 更多