16

I have a dict as follows:

someDict = {'a':[], 'b':[]}

I want to determine if this dictionary has any values which are not empty lists. If so, I want to return True. If not, I want to return False. Any way to make this a one liner?

Nathan
  • 4,545
  • 6
  • 32
  • 49
  • Hm, we're stuck on Python 2.4.1 here, so 'any' won't work, but there are plenty of solutions below which do work. – Nathan May 04 '11 at 21:00

6 Answers6

16

Per my testing, the following one-liner (my original answer) has best time performance in all scenarios. See edits below for testing information. I do acknowledge that solutions using generator expressions will be much more memory efficient and should be preferred for large dicts.

EDIT: This is an aging answer and the results of my testing may not be valid for the latest version of python. Since generator expressions are the more "pythonic" way, I'd imagine their performance is improving. Please do your own testing if you're running this in a 'hot' codepath.

bool([a for a in my_dict.values() if a != []])

Edit:

Decided to have some fun. A comparison of answers, not in any particular order:

(As used below, timeit will calculate a loop order of magnitude based on what will take less than 0.2 seconds to run)

bool([a for a in my_dict.values() if a != []]) :

python -mtimeit -s"my_dict={'a':[],'b':[]}" "bool([a for a in my_dict.values() if a != []])"
1000000 loops, best of 3: 0.875 usec per loop

any([my_dict[i] != [] for i in my_dict]) :

python -mtimeit -s"my_dict={'a':[],'b':[]}" "any([my_dict[i] != [] for i in my_dict])"
1000000 loops, best of 3: 0.821 usec per loop

any(x != [] for x in my_dict.itervalues()):

python -mtimeit -s"my_dict={'a':[],'b':[]}" "any(x != [] for x in my_dict.itervalues())"
1000000 loops, best of 3: 1.03 usec per loop

all(map(lambda x: x == [], my_dict.values())):

python -mtimeit -s"my_dict={'a':[],'b':[]}" "all(map(lambda x: x == [], my_dict.values()))"
1000000 loops, best of 3: 1.47 usec per loop

filter(lambda x: x != [], my_dict.values()):

python -mtimeit -s"my_dict={'a':[],'b':[]}" "filter(lambda x: x != [], my_dict.values())"
1000000 loops, best of 3: 1.19 usec per loop



Edit again - more fun:

any() is best case O(1) (if bool(list[0]) returns True). any()'s worst case is the "positive" scenario - a long list of values for which bool(list[i]) returns False.


Check out what happens when the dict gets big:

bool([a for a in my_dict.values() if a != []]) :

#n=1000
python -mtimeit -s"my_dict=dict(zip(range(1000),[[]]*1000))" "bool([a for a in my_dict.values() if a != []])"
10000 loops, best of 3: 126 usec per loop

#n=100000
python -mtimeit -s"my_dict=dict(zip(range(100000),[[]]*100000))" "bool([a for a in my_dict.values() if a != []])"
100 loops, best of 3: 14.2 msec per loop

any([my_dict[i] != [] for i in my_dict]):

#n=1000
python -mtimeit -s"my_dict=dict(zip(range(1000),[[]]*1000))" "any([my_dict[i] != [] for i in my_dict])"
10000 loops, best of 3: 198 usec per loop

#n=100000
python -mtimeit -s"my_dict=dict(zip(range(100000),[[]]*100000))" "any([my_dict[i] != [] for i in my_dict])"
10 loops, best of 3: 21.1 msec per loop



But that's not enough - what about a worst-case 'False' scenario?

bool([a for a in my_dict.values() if a != []]) :

python -mtimeit -s"my_dict=dict(zip(range(1000),[0]*1000))" "bool([a for a in my_dict.values() if a != []])"
10000 loops, best of 3: 198 usec per loop

any([my_dict[i] != [] for i in my_dict]) :

python -mtimeit -s"my_dict=dict(zip(range(1000),[0]*1000))" "any([my_dict[i] != [] for i in my_dict])"
1000 loops, best of 3: 265 usec per loop
Ben Burns
  • 14,978
  • 4
  • 35
  • 56
13

Not falsey or not empty lists:

Not falsey:

any(someDict.values())

Not empty lists:

any(a != [] for a in someDict.values())

or

any(map(lambda x: x != [], someDict.values()))

Or if you are ok with a falsey return value:

filter(lambda x: x != [], someDict.values())

Returns a list of items that are not empty lists, so if they are all empty lists it's an empty list :)

Henry
  • 6,502
  • 2
  • 24
  • 30
6

Quite literally:

any(x != [] for x in someDict.itervalues())
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
4

try this

 all([d[i] == [] for i in d])

edit: oops, i think i got you backwards. lets deMorgan that

any([d[i] != [] for i in d])

this second way has the short-circuit advantage on the first anyhow

jon_darkstar
  • 16,398
  • 7
  • 29
  • 37
2
>>> someDict = {'a':[], 'b':[]} 
>>> all(map(lambda x: x == [], someDict.values()))
True
bnaul
  • 17,288
  • 4
  • 32
  • 30
  • Favor list comprehension over map, especially w/ lambda -- http://stackoverflow.com/questions/1247486/python-list-comprehension-vs-map/1247490#1247490 – Ben Burns May 04 '11 at 20:49
  • @Ben: `s/list comprehension/generator expression/`. –  May 04 '11 at 20:54
  • @delnan not according to the results I posted in my answer. Care to elaborate on this stance? – Ben Burns May 04 '11 at 21:18
  • For 99.9999% of cases, it's ridiculous to worry about performance here. I find the map syntax to be the most readable, but in retrospect a list comprehension would probably be the "standard" way to do this. – bnaul May 04 '11 at 21:24
  • 1
    Regardless of any tiny speed differences (which vary between versions anyway), the generator version is O(1) in space (it only computes one item at a time) and has potentially much better time complexity than linear, as it never computes more items than necessary, i.e. it will short-circuit as soon as an empty list is encountered (try your benchmarks with the dictionary `dict(('a'*i, [i]) for i in range(10000))`). You also save two brackets ;) –  May 04 '11 at 21:25
  • I hope this doesn't make me seem like a troll, but Guido just voiced his opinion about this the other day. See his comment in response to Michael Foord https://plus.google.com/u/0/115212051037621986145/posts/HajXHPGN752 – Ben Burns Nov 05 '12 at 19:01
2
len(filter(lambda x: x!=[], someDict.values())) != 0
Hai Vu
  • 37,849
  • 11
  • 66
  • 93