30

I wonder if it is possible to gain the same output as from this code:

d = {'a':None,'b':'12345','c':None}
nones=False
for k,v in d.items(): 
  if d[k] is None:
    nones=True      

or

any([v==None for v in d.values()])

but without a for loop iterator, or generator?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
yourstruly
  • 972
  • 1
  • 9
  • 17
  • 4
    You cannot check values in list without iterating on it.. – Maroun Aug 30 '15 at 12:33
  • 1
    You need to check all the values; what exactly is wrong with looping? – Martijn Pieters Aug 30 '15 at 12:33
  • You don't need the list comprehension. Just `any(d.values())`. But that loops internally. There's no way of avoiding that. – juanchopanza Aug 30 '15 at 12:34
  • Robert, take a look at how hash tables work (and are). You might find what you're looking for there. http://stackoverflow.com/questions/327311/how-are-pythons-built-in-dictionaries-implemented – zom-pro Aug 30 '15 at 12:35
  • 1
    Why is this question getting voted down so badly? – zom-pro Aug 30 '15 at 12:36
  • Your version using `any()` contains a list comprehension, not a generator expression. `any()` short-circuits, so it will stop as soon as it's found a True result. That works well on a gen exp, but when you use it on a list comp the complete list is created before the `any` test starts. – PM 2Ring Aug 30 '15 at 12:59

2 Answers2

21

You can use

nones = not all(d.values())

If all values are not None, nones would be set to False, else True. It is just an abstraction though, internally it must iterate over values list.

hspandher
  • 15,934
  • 2
  • 32
  • 45
15

You could have Python do the looping in C code by using a dictionary view; this does a membership test against all values without creating a new list:

if None not in d.values():

(In Python 2, use dict.viewvalues() to get a dictionary view, as dict.values() returns a list there).

Demo on Python 3:

>>> d = {'a': None, 'c': None, 'b': '12345'}
>>> None not in d.values()
False

This will loop over the values until a match is found, just like list membership or a proper any() test, making this an O(N) test. This differs from a dictionary or set membership test, where hashing can be used to give you a fixed cost test on average.

You were not using any() properly; drop the [...] brackets:

if any(v is not None for v in d.values()):  # Python 2: use d.itervalues() or d.viewvalues()

If your goal is to test for multiple values, and you need to avoid constant looping for each test, consider creating an inverse index instead:

inverse_index = {}
for key, value in d.items():
    inverse.setdefault(value, set()).add(key)

This requires the values to be hashable, however. You can now simply test for each value:

if None not in inverse_index:

in O(1) time.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    How about `any(map(lambda x: x is not None, d.values()))` which is more efficient in Python 3 and also works in Python 2? – Niklas R Aug 30 '15 at 12:40
  • @NiklasR: `x is None`, surely. The `lambda` call has costs too, I'm not all that sure that that is so much more efficient as to be worth it. – Martijn Pieters Aug 30 '15 at 12:47
  • @NiklasR it's worse than taking the python3 variant (using `values()`) which will create an ugly list in Python 2 but then use that in a generator expression versus your definitive looping twice, creating an additional list, and thus wasting more. – deets Aug 30 '15 at 12:50
  • @NiklasR: at any rate, using a membership test against a dictionary view is far cheaper (just one method call, everything else is handled in C with a lightweight object). – Martijn Pieters Aug 30 '15 at 12:51
  • I was commenting on the edit of the answer that contained this example: `if set(d.values()) & {None}:`. I think generating a set from the values would be less efficient than stopping on the first occurence of a not-None value with `any()`. The views solution is surely more elegant. :) – Niklas R Aug 31 '15 at 09:07
  • 1
    You could point out that there is no way to avoid the O(N) cost when checking the values of an existing dictionary. But you could use other data structures to avoid it. For example, if the values were also keys in a reverse dictionary then that would be an O(1) test. This might be appropriate to build along with the original dictionary, in some usage scenarios. – strubbly Aug 31 '15 at 09:28
  • 1
    @strubbly: that's a good idea too, and probably the X to the Y problem the OP stated. – Martijn Pieters Aug 31 '15 at 09:36
  • I like @NiklasR's suggestion. I use it within a function to determine if any of the optional keyword arguments are specified. – Mike Finch Oct 19 '22 at 20:04
  • @MikeFinch: `None not in kwargs.values()` (Python 3, `None not in kwargs.viewvalues()` in Python 2) is still more efficient than Niklas's suggestion, the outcome is the same. – Martijn Pieters Nov 25 '22 at 17:08