Vlad
Vlad

Reputation: 301

Python 3.6 iterating through list of dictionaries using filter() with lambda doesn't return dictionary's value

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

Answers (2)

Martijn Pieters
Martijn Pieters

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 lambdas:

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

willeM_ Van Onsem
willeM_ Van Onsem

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 mapping:

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

Related Questions