Filtering a list of dictionaries based on multiple values

。_饼干妹妹 提交于 2021-02-10 12:12:38

问题


I have a list of dictionaries that I would like to filter based on multiple criteria. A shortened version of the list looks like so:

orders = [{"name": "v", "price": 123, "location": "Mars"}, 
          {"name": "x", "price": 223, "location": "Mars"}, 
          {"name": "x", "price": 124, "location": "Mars"}, 
          {"name": "y", "price": 456, "location": "Mars"}, 
          {"name": "z", "price": 123, "location": "Mars"}, 
          {"name": "z", "price": 5623, "location": "Mars"}]

I am looking to end up with a list that contains the dictionaries with the lowest price for each dictionary with the same "name" key. For example, the above would become:

minimums = [{"name": "v", "price": 123, "location": "Mars"},
            {"name": "x", "price": 124, "location": "Mars"},
            {"name": "y", "price": 456, "location": "Mars"},
            {"name": "z", "price": 123, "location": "Mars"}]

I have accomplished this with an abomination of nested if-statements and for-loops, however I was hoping there was a more "Pythonic" way of achieving things.

Either reusing the same list or creating a new one is fine.

Thank you for the help.

EDIT: Thank you for the answers, I tried timing each of them with the following code

print("Number of dictionaries in orders: " + str(len(orders)))

t0 = time.time()
sorted_orders = sorted(orders, key=lambda i: i["name"])
t1 = time.time()
sorting_time = (t1 - t0)

t0 = time.time()
listcomp_wikiben = [x for x in orders if all(x["price"] <= y["price"] for y  in orders if x["name"] == y["name"])]
t1 = time.time()
print("listcomp_wikiben: " + str(t1 - t0))

t0 = time.time()
itertools_MrGeek = [min(g[1], key=lambda x: x['price']) for g in groupby(sorted_orders, lambda o: o['name'])]
t1 = time.time()
print("itertools_MrGeek: " + str(t1 - t0 + sorting_time))

t0 = time.time()
itertools_Cory = [min(g, key=lambda j: j["price"]) for k,g in groupby(sorted_orders, key=lambda i: i["name"])]
t1 = time.time()
print("itertools_CoryKramer: " + str(t1 - t0 + sorting_time))

t0 = time.time()
pandas_Trenton = pd.DataFrame(orders)
pandas_Trenton.groupby(['name'])['price'].min()
t1 = time.time()
print("pandas_Trenton_M: " + str(t1 - t0))

And the results were:

Number of dictionaries in orders: 20867
listcomp_wikiben:     39.78123s
itertools_MrGeek:      0.01562s
itertools_CoryKramer:  0.01565s
pandas_Trenton_M:      0.29685s

回答1:


If you first sort your list by "name", you can use itertools.groupby to group them, then use min with a lambda to find the minimum "price" in each group.

>>> from itertools import groupby
>>> sorted_orders = sorted(orders, key=lambda i: i["name"])
>>> [min(g, key=lambda j: j["price"]) for k,g in groupby(sorted_orders , key=lambda i: i["name"])]
[{'name': 'v', 'price': 123, 'location': 'Mars'},
 {'name': 'x', 'price': 124, 'location': 'Mars'},
 {'name': 'y', 'price': 456, 'location': 'Mars'},
 {'name': 'z', 'price': 123, 'location': 'Mars'}]



回答2:


You can use itertools.groupby:

from itertools import groupby

print([min(g[1], key = lambda x : x['price']) for g in groupby(orders, lambda o : o['name'])])

Output:

[
  {'name': 'v', 'price': 123, 'location': 'Mars'},
  {'name': 'x', 'price': 124, 'location': 'Mars'},
  {'name': 'y', 'price': 456, 'location': 'Mars'},
  {'name': 'z', 'price': 123, 'location': 'Mars'}
]



回答3:


Solution without itertools

[x for x in orders if all(x["price"] <= y["price"] for y in orders if x["name"] == y["name"])]



回答4:


Use pandas:

orders = [{"name": "v", "price": 123, "location": "Mars"}, 
          {"name": "x", "price": 223, "location": "Mars"}, 
          {"name": "x", "price": 124, "location": "Mars"}, 
          {"name": "y", "price": 456, "location": "Mars"}, 
          {"name": "z", "price": 123, "location": "Pluto"}, 
          {"name": "z", "price": 5623, "location": "Mars"}]

import pandas as pd

df = pd.DataFrame(orders)

df.groupby(['name', 'location'])['price'].min()



来源:https://stackoverflow.com/questions/57597433/filtering-a-list-of-dictionaries-based-on-multiple-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!