2

A follow up on the recent question Remove keys from object not in a list in python?

That question turns out to be a duplicate of a previous one. All answers there, and among them the most voted, use list comprehension. I'm thinking on a functional approach. How can this be done using filter?

We have:

testdict={'a':'vala', 'b':'valb', 'c':'valc','d':'vald'}
keep=['a','c']

and I want

filter(isKept,testdict)

to give

{'a':'vala','c':'valc'}

I tried naively defining isKept as a function of either one (the keys) or two variables (keys, values) but the former just filters out the right keys without the corresponding values (i.e., a list, not a dictionary). The latter way doesn't even parse correctly.

Is there a filter for dictionaries in Python?

Notice that testdict.pop(k) is not what I want as this deletes, but the question here is to keep.

MASL
  • 929
  • 1
  • 11
  • 25
  • `filter` is not a good way to accomplish such thing as it always returns a list including filtered values so it doesn't make sense modifying the original list with that. – Ozgur Vatansever Sep 22 '15 at 22:35
  • @ozgur Yes, I kinda noticed that filter doesn't seem to apply to dictionaries, but rather lists. Hence my last question, is there a filter function for dicts that can be used *à la functional*? – MASL Sep 22 '15 at 22:37
  • Yes, but you should convert dictionary to a list first using `dict.items()` – Ozgur Vatansever Sep 22 '15 at 22:39
  • 4
    I think you're somewhat confused on what `functional` in functional programming generally means. – Falmarri Sep 22 '15 at 22:49
  • What is wrong with the dict comprehension? – wim Sep 22 '15 at 22:58
  • @wim nothing wrong per se. And Falmarri , yes, I admit some biased knowledge of it, or simple confusion. I'm sorry that I gave the impression I don't value the answer as list comprehension. One user just deleted his answer and I think now it is pertinent as both of you adequately point out. I was writing to him "PD: I haven't seen many case examples, but the python (or scala) code I usually see used in Spark tends to end up in a composition of maps and filters. That's what (mis)lead my wording in the question." when (s)he deleted the answer. Any way, hope this helps clarify my question. – MASL Sep 22 '15 at 23:08
  • That said, I would like to understand more in which way list comprehension *is* a functional approach. I've seen that before in Haskell, but I always saw it as an (often ugly) recourse of the language. I kept the more naive idea of functions without side-effects and composition of functions as a working definition of *functional* in functional programming. Any pointer to further illustrate this would be appreciated. Thanks a lot! – MASL Sep 22 '15 at 23:25
  • @ozgur Thanks! I see that now. – MASL Sep 22 '15 at 23:26

4 Answers4

4

Truth be told using comprehensions is as functional as it gets, but if that's not what you want toolz library provides a nice set of functions including keyfilter:

>>> from toolz.dicttoolz import keyfilter
>>> to_keep = lambda key: key in set(keep)
>>> keyfilter(to_keep, testdict)
{'a': 'vala', 'c': 'valc'}
zero323
  • 322,348
  • 103
  • 959
  • 935
  • Aha! Didn't know about toolz. Yes, that is exactly what I had in mind. I was thinking on this *compositional* approach because it seems to me more readable when further composing with other functions (say additional maps and filters). – MASL Sep 22 '15 at 22:51
  • I got to give that a closer look. Thanks again for that additional pointer. – MASL Sep 22 '15 at 23:32
3

Functions like filter(), map() and reduce() do not modify the original sequence you pass to them. They return a new sequence or value. In addition to that, if you want to use filter() on dicts, you should convert it to list first using .items() or better yet, to an iterator via .iteritems():

>>> testdict = {'a':'vala', 'b':'valb', 'c':'valc','d':'vald'}
>>> keep = ['a','c']
>>> print dict(filter(lambda entry: entry[0] in keep, testdict.items()))
{'a': 'vala', 'c': 'valc'}
Ozgur Vatansever
  • 49,246
  • 17
  • 84
  • 119
  • Thanks a lot! That seems way closer to what I had in mind, albeit python is kind of cheating here as first we need to translate the dict into a list of tuples. Does it mean there really is no "natural" filter function for dicts and we need to hack our own -e.g., as you show us here? – MASL Sep 22 '15 at 22:46
  • No, because in functional programming languages like scheme, lisp etc., these functions are applied to a series of values and produce another set of values. They don't modify the given input sequence in-place. – Ozgur Vatansever Sep 22 '15 at 23:06
  • Not sure I follow you here. I'd say map(function,list) in python behaves just like that, i.e., it doesn't modify list. Your own answer doesn't modify testdict either. So, it seems we do have "strict functions" in python. – MASL Sep 22 '15 at 23:20
  • Sorry, I misunderstood it. Your question's answer is No. There is no builtin filter function which operates on dicts. This is why there is a 3rd party function named `toolz.keyfilter` which basically operates on just keys. – Ozgur Vatansever Sep 22 '15 at 23:23
  • Thanks again! I think that completes the answer to my original question. I don't think I can accept two or more answers, and it seems only fair to do so for zero323 as it explicitly constructs the solution I asked for. Hope you don't see this as disregarding your contribution. It isn't! – MASL Sep 22 '15 at 23:30
2

A dict comprehension is as functional as it gets (i.e. it is both declarative and stateless):

{k: v for k, v in d.items() if k in keep}

Furthermore, it is pythonic (i.e. it is explicit, readable, and succinct). You really should prefer this style.

Joel Cornett
  • 24,192
  • 9
  • 66
  • 88
0
testdict={'a':'vala', 'b':'valb', 'c':'valc','d':'vald'}
keep=['a','c']

def isKept(dict_item):
  if dict_item[0] in keep:
    return True
  else:
    return False

print(dict(filter(isKept,testdict.items())))
Fuji Komalan
  • 1,979
  • 16
  • 25