Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter list of dictionaries by key value - return answers as a list of dictionaries

I have a list of dictionaries that I want to filter.

[{"Slope": -0.562, "Count": 3},
 {"Slope": -0.362, "Count": 6},
 {"Slope": -0.762, "Count": 8},
 {"Slope": -0.562, "Count": 12},
 {"Slope": 2.5, "Count": 34},
 {"Slope": 1.52, "Count": 2},
 {"Slope": .56, "Count": 6}]

My goal is to get a list of two dictionaries. One with the "highest count and a POSITVE slope" and the other with the "highest count and NEGATIVE slope".

My plan was to filter all the positive and negative ones out, then sort each list and then create a new list with the first record of each.

Sorting the list isnt an issue for me, i've got this!

lines_lst.sort(key=lambda i: i['lines_count'])

But filtering doesn't seem to work when I try this as it returns a dictionary.

positive_lines = next(item for item in lines_lst if item["Slope"] > 0)

Does anyone have a solution that ends up with the below?

[{"Slope": -0.562, "Count": 12},{"Slope": 2.5, "Count": 34}]
like image 307
Lewis Morris Avatar asked Dec 17 '22 15:12

Lewis Morris


2 Answers

You want max and min .. use them and apply a suitable key function - in fact using tuples you only need max:

data = [{"Slope": -0.562, "Count": 3},
        {"Slope": -0.362, "Count": 6},
        {"Slope": -0.762, "Count": 8},
        {"Slope": -0.562, "Count": 12},
        {"Slope": 2.5, "Count": 34},
        {"Slope": 1.52, "Count": 2},
        {"Slope": .56, "Count": 6}]

m1 = max(data, key= lambda x: (x["Slope"]>0, x["Count"]))
m2 = max(data, key= lambda x: (x["Slope"]<0, x["Count"]))

result = [m1,m2]

print(result)

Output:

[{'Slope': 2.5, 'Count': 34}, {'Slope': -0.562, 'Count': 12}]

Tuples sort by 1st value, then second value - you can build tuples and use them as max key function.

like image 131
Patrick Artner Avatar answered Dec 28 '22 10:12

Patrick Artner


You can pass a generator expression to max():

>>> max((d for d in lines_lst if d["Slope"] > 0), key=lambda d: d["Count"])
{'Slope': 2.5, 'Count': 34}
>>> max((d for d in lines_lst if d["Slope"] < 0), key=lambda d: d["Count"])
{'Slope': -0.562, 'Count': 12}

Granted, this solution iterates through lines_lst twice. If you have a really large input, you could iterate through it once, greedily, keeping track of the running max/min:

import sys

max_pos, max_neg = {"Count": -sys.maxsize}, {"Count": -sys.maxsize}
for d in lines_lst:
    ct = d["Count"]
    if d["Slope"] > 0 and ct > max_pos["Count"]:
        max_pos = d
    elif d["Slope"] < 0 and ct > max_neg["Count"]:
        max_neg = d

But in Python-land, this is probably only valuable if your input is really large and unwieldy.

Note that in both these cases, _further modifications to max_pos/max_neg constitute modifications to the members of lines_lst, because those members are mutable dictionaries.

like image 33
Brad Solomon Avatar answered Dec 28 '22 09:12

Brad Solomon