How to remove a key/value pair in python dictionary? - python

Say I have a dictionary like this :
d = {'ben' : 10, 'kim' : 20, 'bob' : 9}
Is there a way to remove a pair like ('bob',9) from the dictionary?
I already know about d.pop('bob') but that will remove the pair even if the value was something other than 9.
Right now the only way I can think of is something like this :
if (d.get('bob', None) == 9):
d.pop('bob')
but is there an easier way? possibly not using if at all

pop also returns the value, so performance-wise (as neglectable as it may be) and readability-wise it might be better to use del.
Other than that I don't think there's something easier/better you can do.
from timeit import Timer
def _del():
d = {'a': 1}
del d['a']
def _pop():
d = {'a': 1}
d.pop('a')
print(min(Timer(_del).repeat(5000, 5000)))
# 0.0005624240000000613
print(min(Timer(_pop).repeat(5000, 5000)))
# 0.0007729860000003086

You want to perform two operations here
1) You want to test the condition d['bob']==9.
2) You want to remove the key along with value if the 1st answer is true.
So we can not omit the testing part, which requires use of if, altogether. But we can certainly do it in one line.
d.pop('bob') if d.get('bob')==9 else None

Related

Is it okay to nest a dict.get() inside another or is this bad design?

So, I am working on a code base where a dictionary contains some key information. At some point in the development process the name of one of the keys was changed, but the older key still exists in a lot of places. Lets call the keys new and old for reference.
In order to make it compatible with the older version, I am doing something like:
dict_name.get(new_key,dict_name.get(old_key,None))
Is this bad design or is it okay? Why/Why not?
Example for clarification: (Based on input by #Alexander)
There are two dictionaries d1 and d2.
d1={k1:v1,old_key:some_value}
d2={k1:v1,new_key:some_value}
The function which I am designing right now could get either d1 or d2 like dictionary as an argument. My function should be able to pick up some_value, regardless of whether old_key or new_key is present.
That is a reasonable approach. The only downside is that it will perform the get for both keys, which will not affect performance in most situations.
My only notes are nitpicks:
dict is a reserved word, so don't use it as a variable
None is the default, so it can be dropped for old_key, e.g.:
info.get('a', info.get('b'))
In response to "Is there a way to prevent the double call?": Yup, several reasonable ways exist =).
The one-liner would probably look like:
info['a'] if 'a' in info else info.get('b')
which starts to get difficult to read if your keys are longer.
A more verbose way would be to expand it out into full statements:
val = None
if 'a' in info:
val = info['a']
elif 'b' in info:
val = info['b']
And finally a generic option (default after *keys) will only work with python 3):
def multiget(info, *keys, default=None):
''' Try multiple keys in order, or default if not present '''
for k in keys:
if k in info:
return info[k]
return default
which would let you resolve multiple invocations cleanly, e.g.:
option_1 = multiget(info, 'a', 'b')
option_2 = multiget(info, 'x', 'y', 'z', default=10)
If this is somehow a pandemic of multiple api versions or something (?) you could even go so far as wrapping dict, though it is likely to be overkill:
>>> class MultiGetDict(dict):
... def multiget(self, *keys, default=None):
... for k in keys:
... if k in self:
... return self[k]
... return default
...
>>> d = MultiGetDict({1: 2})
>>> d.multiget(1)
2
>>> d.multiget(0, 1)
2
>>> d.multiget(0, 2)
>>> d.multiget(0, 2, default=3)
3
dict.get is there for exactly this reason, so you can fall back on default values if the keys are not in there.
Having a double fallback is very much OK. For example:
d = {}
result = d.get('new_key',d.get('old_key', None))
This would mean that result is None in the worse case, but there is no error (which is the goal of get in the first place.
In other words, it will get the value of new_key as a first priority, old_key as the second priority, and None as a third.
Also worth noting that get(key, None) is the same as get(key) so you might want to shorten that line:
result = d.get('new_key', d.get('old_key'))
If you want to avoid calling get multiple times (for example, if you have to do more than 2 of those, it will be unreadable) you can do something like this:
priority = ('new_key', 'old_key', 'older_key', 'oldest_key')
for key in priority:
result = d.get(key)
if result is not None:
break
And result becomes whatever is encountered first in that loop, or None otherwise
Based on the sample dictionary provided, I would argue that this is bad design...
Lets say your original dictionary is:
d1 = {'k1': 1, 'k2': 2}
If I understand you correctly, you then 'update' one of the keys, e.g.:
d1 = {'k3': 1, 'k2': 2}
If you try to access via:
d1.get('k3', d1.get('k1')) # 'k3' is new key, 'k1' is old key.
then the first lookup will always be present and the second lookup will never be used.
If you meant that the new dictionary would looks like:
d2 = {'k1': 1, 'k2': 2, 'k3': 1}
then you are storing the 'same' data in two different locations in your dictionary, which will surely lead to trouble (similar to normalized data in a database). For example, if the value of 'k3' was updated to 3, then the value of k1 would need to be updated as well.
Given the dictionaries provided in your example:
d1={k1: v1, old_key: some_value}
d2={k1: v1, new_key: some_value}
I assume that some_value are intended to be equal in both, i.e. d1[old_key] == d2[new_key]. If so, then you could use d2.get(new_key, d1.get(old_key). However, it just seems like a mess.
If some_value needs to be updated, for example, it must be updated in both dictionaries.
You are wasting memory by storing the some_value twice.
Your new_key in d2 may accidentally clobber an existing key in d1.
I would recommend not changing the key names in the first place.

Python using string in dict() key?

Let say I have this base dictionary:
main = {'a': 10}
Then I have two other dictionaries which use main as starting point like this:
some_new_dict1 = dict(main, b=20, some_other_key=100)
some_new_dict2 = dict(main, c=20, some_other_key2=200)
But the thing is for b and for c keys, those are not fixed. It depends on other values.
In my case to evaluate what kind of key to assign to which dictionary is done like this:
some_map = {True: 'b', False: 'c'}
# Evaluate if its true or false
answer = something > 0 # something is variable that changes.
#It can be greater than zero or lower than zero (but not zero).
dict1_key, dict2_key = some_map[answer], some_map[not answer]
Now I have these keys in string form, but I don't see how can I assign it through dict. If its even possible.
So now I'm doing this:
some_new_dict1 = dict(main, some_other_key=100)
some_new_dict1[dict1_key] = 20
# Same for another dict
So basically I need to create dictionary and then update it with that one key/value pair even though I know that key/value pair before creating that dictionary. Is there some better way?
main = {'a': 5}
dict_key1 = 'test_key'
dict_val1 = 5
some_new_dict = dict(main, other_key=5, **{dict_key1: dict_val1})
Although, I find the two step way you use now more readable, so I think it's better.
If you want to opt for readability you can use copy, and perhaps also pack your mapping into the line:
some_new_dict1 = main.copy()
some_new_dict1[some_other_key] = 100
some_new_dict1['b' if answer else 'c'] = 20
By the way your comment #It can be greater than zero or lower than zero (but not zero). is false for your dict2_key. [not answer] includes zero, if it makes a difference for the logic of your programme.

Python: parsing a value(string) in a dictionary and replace it

my dictionary:
mydict{'a':'/1/2/3/4/5/wrong1', 'b':'/x/y/x/u/t/wrong2'}
I would like parse the value, and replace 'wrong*' with 'right'. 'Right' is always the same, whereas 'wrong' is different each time.
'wrong' looks e.g. like that: folder_x/folder_y/somelongfilename.gz
'right' looks like that: *-AAA/debug.out
so afterwards my dictionary should look like that:
mydict{'a':'/1/2/3/4/5/right', 'b':'/x/y/x/u/right'}
Just replacing the value won't work here because I want to parse the value and replace only the last part of it. It is important to keep the first part of the value.
Does anyone have an idea how to solve this.
Thank you.
You could use a re.sub to handle the replacement for you
>>> import re
>>> {k : re.sub('wrong', 'right', v) for k,v in mydict.items()}
{'b': '/x/y/x/u/t/right2',
'a': '/1/2/3/4/5/right1'}

Declaring a dictionary using another value within the same dictionary?

I'm using python trying to basically do this:
myDict = {"key1" : 1, "key2" : myDict["key1"]+1}
...if you catch my drift. Possible without using multiple statements?
EDIT: Also, if anyone could tell me a better way to state this question more clearly that would be cool. I don't really know how to word what I'm asking.
EDIT2: Seems to be some confusion - yes, it's more complex than just "key2":1+1, and what I'm doing is mostly for code readability as it will get messy if I have to 2-line it.
Here's a bit more accurate code sample of what I'm trying to do...though it's still not nearly as complex as it gets :P
lvls={easy: {mapsize:(10,10), winPos:(mapsize[0]-1,mapsize[1]-1)},
medium:{mapsize:(15,15), winPos:(mapsize[0]-RANDOMINT,mapsize[1]-1)},
hard: {mapsize:(20,20), winPos:(mapsize[0]-RANDOMINT,mapsize[1]-RANDOMINT)}
}
No, this isn't possible in general without using multiple statements.
In this particular case, you could get around it in a hacky way. For example:
myDict = dict(zip(("key1", "key2"), itertools.count(1))
However, that will only work when you want to specify a single start value and everything else will be sequential, and presumably that's not general enough for what you want.
If you're doing this kind of thing a lot, you could wrap those multiple statements up in some suitably-general function, so that each particular instance is just a single expression. For example:
def make_funky_dict(*args):
myDict = {}
for key, value in zip(*[iter(a)]*2):
if value in myDict:
value = myDict[value] + 1
myDict[key] = value
return myDict
myDict = make_funky_dict("key1", 1, "key2", "key1")
But really, there's no good reason not to use multiple statements here, and it will probably be a lot clearer, so… I'd just do it that way.
It's not possible without using multiple statements, at least not using some of the methods from your problem statement. But here's something, using dict comprehension:
>>> myDict = {"key" + str(key): value for (key, value) in enumerate(range(7))}
>>> myDict
{'key0': 0,
'key1': 1,
'key2': 2,
'key3': 3,
'key4': 4,
'key5': 5,
'key6': 6}
Of course those aren't in order, but they're all there.
The only variable you are trying to use is an integer. How about a nice function:
def makelevel(size,jitterfunc=lambda:0):
return {'mapsize':(size,size), 'winPos':(size-1+jitterfunc(),size-1+jitterfunc())}
lvls = {hardness,makelevel(size) for hardness, size in [('easy',10),('medium',15), ('hard',20)]}
Of course, this function looks a bit like a constructor. Maybe you should be using objects?
If you want a dict that will allow you to to have values that are evaluated on demand you can do something like this:
class myDictType(dict):
def __getitem__(self, key):
retval = dict.__getitem__(self,key)
if type(retval) == type(lambda: 1):
return retval()
return retval
myDict = myDictType()
myDict['bar'] = lambda: foo['foo'] + 1
myDict['foo'] = 1
print myDict['bar'] #This'll print a 2
myDict['foo'] = 2
print myDict['bar'] #This'll print a 3
This overrides __getitem__ in the dictionary to return whatever is stored in it (like a normal dictionary,) unless what is stored there is a lambda. If the value is a lambda, it instead evaluates it and returns the result.

Check for a key pattern in a dictionary in python

dict1=({"EMP$$1":1,"EMP$$2":2,"EMP$$3":3})
How to check if EMP exists in the dictionary using python
dict1.get("EMP##") ??
It's not entirely clear what you want to do.
You can loop through the keys in the dict selecting keys using the startswith() method:
>>> for key in dict1:
... if key.startswith("EMP$$"):
... print "Found",key
...
Found EMP$$1
Found EMP$$2
Found EMP$$3
You can use a list comprehension to get all the values that match:
>>> [value for key,value in dict1.items() if key.startswith("EMP$$")]
[1, 2, 3]
If you just want to know if a key matches you could use the any() function:
>>> any(key.startswith("EMP$$") for key in dict1)
True
This approach strikes me as contrary to the intent of a dictionary.
A dictionary is made up of hash keys which have had values associated with them. The benefit of this structure is that it provides very fast lookups (on the order of O(1)). By searching through the keys, you're negating that benefit.
I would suggest reorganizing your dictionary.
dict1 = {"EMP$$": {"1": 1, "2": 2, "3": 3} }
Then, finding "EMP$$" is as simple as
if "EMP$$" in dict1:
#etc...
You need to be a lot more specific with what you want to do. However, assuming the dictionary you gave:
dict1={"EMP$$1":1, "EMP$$2":2, "EMP$$3":3}
If you wanted to know if a specific key was present before trying to request it you could:
dict1.has_key('EMP$$1')
True
Returns True as dict1 has the a key EMP$$1.
You could also forget about checking for keys and rely on the default return value of dict1.get():
dict1.get('EMP$$5',0)
0
Returns 0 as default given dict1 doesn't have a key EMP$$5.
In a similar way you could also use a `try/except/ structure to catch and handle missed keys:
try:
dict1['EMP$$5']
except KeyError, e:
# Code to deal w key error
print 'Trapped key error in dict1 looking for %s' % e
The other answers to this question are also great, but we need more info to be more precise.
There's no way to match dictionary keys like this. I suggest you rethink your data structure for this problem. If this has to be extra quick you could use something like a suffix tree.
You can use in string operator that checks if item is in another string. dict1 iterator returns list of keys, so you check "EMP$$" against of each dict1.key.
dict1 = {"EMP$$1": 1, "EMP$$2": 2, "EMP$$3": 3}
print(any("EMP$$" in i for i in dict1))
# True
# testing for item that doesn't exist
print(any("AMP$$" in i for i in dict1))
# False

Categories