Reputation: 301
There is a list of dictionaries I need to iterate through and filter in dictionaries which meet criterion and then return only values under key1. I'm using filter, as follows:
res = list(filter(lambda x: x["key1"] if ["key2"] == criterion else False, list_of_dicts))
the iterator works alright returning only those results which meet criterion. But it returns the whole dictionary rather than only x["key1"]
.
I'm thinking it's something to do with filter but have no clue.
Q1: Can someone explain why it returns whole dict rather then only value under key1?
Q2: is there a workaround to make filter to return only value under key1?
P.S. there is no problem to do it through list comprehension like that:
res = [i["key1"] for i in list_of_dicts if i["key2"] == criterion]
But I was just curious why filter doesn't return i["key1"]
.
Upvotes: 1
Views: 3094
Reputation: 1122182
Filtering is not the same thing as mapping. Filtering selects based on your criteria, a yes/no decision on whether or not to include one of your values.
In fact, you are filtering on both key2
and the truth value of key1
; if the value for key1
is ever set to None
or 0
or another false value, you'd also drop that specific dictionary, which I don't think you really wanted.
You want to filter and map, so extract the right key from the dictionaries that passed the filter. That's actually best done with a list comprehension instead:
res = [x["key1"] for x in list_of_dicts if x["key2"] == criterion]
You can still do it using filter()
, if you must, but then you need to add a map()
call to this to actually do the mapping:
res = list(map(lambda x: x["key1"], filter(lambda x: ["key2"], list_of_dicts)))
But as you notice, that gets verbose and unreadable really quickly. You could use operator.itemgetter()
objects to replace the lambda
s:
from operator import itemgetter
res = list(map(itemgetter("key1"), filter(lambda d: d["key2"] == condition, list_of_dicts)))
but readability doesn't improve much with that.
Upvotes: 7
Reputation: 476699
A filter simply filters: it takes as input an iterable, and produces an interable with the items that satisfy the criterion.
What you want is an additional map
ping:
from operator import itemgetter
key1 = itemgetter('key1')
res = list(map(key1,filter(lambda x: ["key2"] == criterion, list_of_dicts)))
Mind that you do not need to use x['key1'] if ... else False
in the criterion: the filter(..)
will simply evaluate the function, and inspect the truthiness of the result. If it is True
, it will emit the item, otherwise it will ignore the item.
Upvotes: 6