sniperd
sniperd

Reputation: 5274

Test if dictionary key exists, is not None and isn't blank

I have code that works but I'm wondering if there is a more pythonic way to do this. I have a dictionary and I want to see if:

So in my code the keys of "a", "b", and "c" would succeed, which is correct.

import re

mydict = {
"a":"alpha",
"b":0,
"c":False,
"d":None,
"e":"",
"g":"   ",
}

#a,b,c should succeed
for k in mydict.keys():
    if k in mydict and mydict[k] is not None and not re.search("^\s*$", str(mydict[k])):
        print(k)
    else:
        print("I am incomplete and sad")

What I have above works, but that seems like an awfully long set of conditions. Maybe this simply is the right solution but I'm wondering if there is a more pythonic "exists and has stuff" or better way to do this?

UPDATE Thank you all for wonderful answers and thoughtful comments. With some of the points and tips, I've updated the question a little bit as there some conditions I didn't have which should also succeed. I have also changed the example to a loop (just easier to test right?).

Upvotes: 20

Views: 35103

Answers (8)

Kevin Chen
Kevin Chen

Reputation: 91

I see that accepted answer it is a very good point for illustration for None or blank string

personally i think it'd be a simply solution by

mydict.get('a', '') 
>>> alpha

mydict.get('k', '')
>>> ''

this would be simple and straightforward.

Upvotes: 0

HyperionX
HyperionX

Reputation: 1646

Here's a simple one-liner to check:

  • The key exists
  • The key is not None
  • The key is not ""
bool(myDict.get("some_key"))

As for checking if the value contains only spaces, you would need to be more careful as None doesn't have a strip() method.

Something like this as an example:

try:
    exists = bool(myDict.get('some_key').strip())
except AttributeError:
    exists = False

Upvotes: 3

Jean-François Fabre
Jean-François Fabre

Reputation: 140168

Try to fetch the value and store it in a variable, then use object "truthyness" to go further on with the value

v = mydict.get("a")
if v and v.strip():
  • if "a" is not in the dict, get returns None and fails the first condition
  • if "a" is in the dict but yields None or empty string, test fails, if "a" yields a blank string, strip() returns falsy string and it fails too.

let's test this:

for k in "abcde":
    v = mydict.get(k)
    if v and v.strip():
        print(k,"I am here and have stuff")
    else:
        print(k,"I am incomplete and sad")

results:

a I am here and have stuff
b I am incomplete and sad    # key isn't in dict
c I am incomplete and sad    # c is None
d I am incomplete and sad    # d is empty string
e I am incomplete and sad    # e is only blanks

if your values can contain False, 0 or other "falsy" non-strings, you'll have to test for string, in that case replace:

if v and v.strip():

by

if v is not None and (not isinstance(v,str) or v.strip()):

so condition matches if not None and either not a string (everything matches) or if a string, the string isn't blank.

Upvotes: 25

marsouf
marsouf

Reputation: 1147

cond is a generator function responsible for generating conditions to apply in a short-circuiting manner using the all function. Given d = cond(), next(d) will check if a exists in the dict, and so on until there is no condition to apply, in that case all(d) will evaluate to True.

mydict = {
  "a":"alpha",
  "c":None,
  "d":"",
  "e":"   ",
}

def cond ():
  yield 'a' in mydict
  yield mydict ['a']
  yield mydict ['a'].strip ()

if all (cond ()):
    print("I am here and have stuff")
else:
    print("I am incomplete and sad")

Upvotes: 0

ewwink
ewwink

Reputation: 19154

it check exactly for NoneType not only None

from types import NoneType # dont forget to import this

mydict = {
"a":"alpha",
"b":0,
"c":False,
"d":None,
"e":"",
"g":"   ",
}

#a,b,c should succeed
for k in mydict:
    if type(mydict[k]) != NoneType:
        if type(mydict[k]) != str or type(mydict[k]) == str and mydict[k].strip():
            print(k)
        else:
            print("I am incomplete and sad")
    else:
        print("I am incomplete and sad")

Upvotes: 0

Adam J
Adam J

Reputation: 1450

Well I have 2 suggestions to offer you, especially if your main issue is the length of the conditions.

The first one is for the check if the key is in the dict. You don't need to use "a" in mydict.keys() you can just use "a" in mydict.

The second suggestion to make the condition smaller is to break down into smaller conditions stored as booleans, and check these in your final condition:

import re

mydict = {
"a":"alpha",
"c":None,
"d":"",
"e":"   ",
}

inKeys = True if "a" in mydict else False
isNotNone = True if mydict["a"] is not None else False
isValidKey = True if not re.search("^\s*$", mydict["a"]) else False

if inKeys and isNotNone and isValidKey:
    print("I am here and have stuff")
else:
    print("I am incomplete and sad")

Upvotes: 1

jpp
jpp

Reputation: 164673

You can use a list comprehension with str.strip to account for whitespace in strings.

Using if v is natural in Python to cover False-like objects, e.g. None, False, 0, etc. So note this only works if 0 is not an acceptable value.

res = [k for k, v in mydict.items() if (v.strip() if isinstance(v, str) else v)]

['a']

Upvotes: 2

Kevin K.
Kevin K.

Reputation: 1397

The get method for checking if a key exists is more efficient that iterating through the keys. It checks to see if the key exists without iteration using an O(1) complexity as apposed to O(n). My preferred method would look something like this:

if mydict.get("a") is not None and str(mydict.get("a")).replace(" ", "") != '':
    # Do some work

Upvotes: 2

Related Questions