简体   繁体   English

迭代两个dicts列表并创建没有循环的元组列表

[英]Iterate over two lists of dicts and create list of tuples without loop

I have two lists of dicts: list1 and list2 . 我有两个dicts列表: list1list2

print(list1)
[{'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
 {'name': 'bard', 'desc': 'besd', 'city': 2, 'ID': 1},
 {'name': 'baer', 'desc': 'bees', 'city': 2, 'ID': 1},
 {'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
 {'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2}]

print(list2)
[{'name': 'foo', 'desc': 'baz', 'city': 1, 'ID': 1},
 {'name': 'bar', 'desc': 'bes', 'city': 1, 'ID': 1},
 {'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1},
 {'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2},
 {'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2}]

I need a list of tuples that will hold two paired dicts (one dict from each list) with the same city and ID . 我需要一个元组列表,它将包含两个配对的dicts(每个列表中的一个dict),具有相同的cityID

I did it with double loop: 我用双循环做到了:

list_of_tuples = []
for i in list1:
    for j in list2:
        if i['ID'] == j['ID'] and i['city'] == j['city']:
            list_of_tuples.append((i, j))
print(list_of_tuples)

[({'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
  {'name': 'foo', 'desc': 'baz', 'city': 1, 'ID': 1}),
 ({'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
  {'name': 'bar', 'desc': 'bes', 'city': 1, 'ID': 1}),
 ({'name': 'bard', 'desc': 'besd', 'city': 2, 'ID': 1},
  {'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1}),
 ({'name': 'baer', 'desc': 'bees', 'city': 2, 'ID': 1},
  {'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1}),
 ({'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
  {'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2}),
 ({'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
  {'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2}),
 ({'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2},
  {'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2}),
 ({'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2},
  {'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2})]

Question: How to do this in a more pythonic way (without loops)? 问题:如何以更加pythonic的方式(没有循环)这样做?

You can use itertools.product and filter : 您可以使用itertools.productfilter

from itertools import product


list1 = [{'name': 'fooa', 'desc': 'bazv', 'city': 1, 'ID': 1},
         {'name': 'bard', 'desc': 'besd', 'city': 2, 'ID': 1},
         {'name': 'baer', 'desc': 'bees', 'city': 2, 'ID': 1},
         {'name': 'aaaa', 'desc': 'bnbb', 'city': 1, 'ID': 2},
         {'name': 'cgcc', 'desc': 'dgdd', 'city': 1, 'ID': 2}]

list2 = [{'name': 'foo', 'desc': 'baz', 'city': 1, 'ID': 1},
         {'name': 'bar', 'desc': 'bes', 'city': 1, 'ID': 1},
         {'name': 'bar', 'desc': 'bes', 'city': 2, 'ID': 1},
         {'name': 'aaa', 'desc': 'bbb', 'city': 1, 'ID': 2},
         {'name': 'ccc', 'desc': 'ddd', 'city': 1, 'ID': 2}]

def condition(x):
    return x[0]['ID'] == x[1]['ID'] and x[0]['city'] == x[1]['city']

list_of_tuples = list(filter(condition, product(list1, list2)))

This is a problem well suited for pandas . 这是一个非常适合pandas的问题。 If you convert the lists to DataFrames, matching the records on ID and city is the same as an inner join of the two DataFrames . 如果将列表转换为DataFrame,则匹配IDcity上的记录与两个DataFrame内部联接相同

import pandas as pd

# convert lists to DataFrames
df1 = pd.DataFrame(list1)
df2 = pd.DataFrame(list2)

# merge the two DataFrames
print(df1.merge(df2, on=["ID", "city"]))
#   ID  city desc_x name_x desc_y name_y
#0   1     1   bazv   fooa    baz    foo
#1   1     1   bazv   fooa    bes    bar
#2   1     2   besd   bard    bes    bar
#3   1     2   bees   baer    bes    bar
#4   2     1   bnbb   aaaa    bbb    aaa
#5   2     1   bnbb   aaaa    ddd    ccc
#6   2     1   dgdd   cgcc    bbb    aaa
#7   2     1   dgdd   cgcc    ddd    ccc

Now you have the matched records in each row. 现在,每行都有匹配的记录。 Since the desc and name columns were present in both (and not used for the merge), they get subscripted with _x and _y to differentiate between the two souce DataFrames. 由于descname列存在于两者中(并且不用于合并),因此它们使用_x_y进行下标以区分两个源DataFrame。

You just need to reformat it to be in your desired output. 您只需将其重新格式化为所需的输出即可。 You can achieve this using to_dict and a list comprehension: 你可以使用to_dict和列表理解来实现这个to_dict

list_of_tuples = [
    (
        {"name": r["name_x"], "desc": r["desc_x"], "city": r["city"], "ID": r["ID"]},
        {"name": r["name_y"], "desc": r["desc_y"], "city": r["city"], "ID": r["ID"]}
    ) for r in df1.merge(df2, on=["ID", "city"]).to_dict(orient="records")
]

print(list_of_tuples)
#[({'ID': 1, 'city': 1, 'desc': 'bazv', 'name': 'fooa'},
#  {'ID': 1, 'city': 1, 'desc': 'baz', 'name': 'foo'}),
# ({'ID': 1, 'city': 1, 'desc': 'bazv', 'name': 'fooa'},
#  {'ID': 1, 'city': 1, 'desc': 'bes', 'name': 'bar'}),
# ({'ID': 1, 'city': 2, 'desc': 'besd', 'name': 'bard'},
#  {'ID': 1, 'city': 2, 'desc': 'bes', 'name': 'bar'}),
# ({'ID': 1, 'city': 2, 'desc': 'bees', 'name': 'baer'},
#  {'ID': 1, 'city': 2, 'desc': 'bes', 'name': 'bar'}),
# ({'ID': 2, 'city': 1, 'desc': 'bnbb', 'name': 'aaaa'},
#  {'ID': 2, 'city': 1, 'desc': 'bbb', 'name': 'aaa'}),
# ({'ID': 2, 'city': 1, 'desc': 'bnbb', 'name': 'aaaa'},
#  {'ID': 2, 'city': 1, 'desc': 'ddd', 'name': 'ccc'}),
# ({'ID': 2, 'city': 1, 'desc': 'dgdd', 'name': 'cgcc'},
#  {'ID': 2, 'city': 1, 'desc': 'bbb', 'name': 'aaa'}),
# ({'ID': 2, 'city': 1, 'desc': 'dgdd', 'name': 'cgcc'},
#  {'ID': 2, 'city': 1, 'desc': 'ddd', 'name': 'ccc'})]

Having nested loops is not "not pythonic". 嵌套循环不是“不是pythonic”。 However, you can achieve the same result with a list comprehension. 但是,您可以使用列表推导实现相同的结果。 I don't think it's more readable though: 我不认为它更具可读性:

[(i, j) for j in list2 for i in list1 if i['ID'] == j['ID'] and i['city'] == j['city']]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM