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

8

19

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:

  • a key exists
  • that value isn't None (NULL from SQL in this case)
  • that value isn't simply quote quote (blank?)
  • that value doesn't solely consist of spaces

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?).

Pisistratus answered 3/5, 2018 at 13:42 Comment(7)
How about if mydict.get("a", "").strip():? None and "" are both falsey.Promptitude
@Promptitude That fails if mydict['a'] is None, as None doesn't have a strip method.Wallah
@BradSolomon just one, no need to do a loopPisistratus
Just a small note - you can just do "a" in mydict without having to call mydict.keys()Lessen
Note that this is a use case covered by PEP-505, although it is currently deferred, so it is not of immediate use.Wallah
Have you considered cleaning up the data in the dict as you create it? (For example, replace the space-only strings with "" so you don't have to consider them separately later.)Wallah
@Wallah that's an interesting outside the box idea. I could indeed just ISNULL(column, '') on my SQL side to eliminate the None conditionPisistratus
F
24

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.

Ferrick answered 3/5, 2018 at 13:48 Comment(0)
S
3

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
Servitude answered 2/12, 2021 at 13:13 Comment(0)
A
2

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
Anopheles answered 3/5, 2018 at 13:47 Comment(0)
F
2

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']
Farmhand answered 3/5, 2018 at 13:48 Comment(2)
Good call on the 0. I can't guarantee that the value won't be 0Pisistratus
You could add an additional clause: res = [k for k, v in mydict.items() if (v == 0) or (v.strip() if isinstance(v, str) else v)]. But now you're getting to the stage where an explicit function is better than a one-liner.Farmhand
G
1

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")
Gadgetry answered 3/5, 2018 at 13:51 Comment(0)
C
0

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")
Charlena answered 3/5, 2018 at 13:57 Comment(2)
this works ok from a functional programming perspective but is arguably not very pythonic and wouldn't handle the case where mydict['a'] is NoneRadie
@Radie definitely, it would not handle even the case of 'a' not in mydict.Charlena
K
0

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")
Keble answered 3/5, 2018 at 15:6 Comment(0)
T
0

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.

Tutto answered 1/2 at 20:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.