I'm trying to build a method where if an item is not in a dictionary then it uses the last member of a list and updates the dictionary accordingly. Sort of like a combination of the pop and setdefault method. What I tried was the following:
dict1 = {1:2,3:4,5:6}
b = 7
c = [8,9,10]
e = dict1.setdefault(b, {}).update(pop(c))
So I would like the output to be where {7:10} gets updated to dict1, that is to say, if b is not in the keys of dict1 then the code updates dict1 with an item using b and the last item of c.
It might be possible for you to abuse a defaultdict:
from collections import defaultdict
c = [8, 9, 10]
dict1 = defaultdict(c.pop, {1: 2, 3: 4, 5: 6})
b = 7
e = dict1[b]
This will pop an item from c and make it a value of dict1 whenever a key missing from dict1 is accessed. (That means the expression dict1[b] on its own has side-effects.) There are many situations where that behaviour is more confusing than helpful, though, in which case you can opt for explicitness:
if b in dict1:
e = dict1[b]
else:
e = dict1[b] = c.pop()
which can of course be wrapped up in a function:
def get_or_pop(mapping, key, source):
if key in mapping:
v = mapping[key]
else:
v = mapping[key] = source.pop()
return v
⋮
e = get_or_pop(dict1, b, c)
Considering your variables, you could use the following code snippet
dict1[b] = dict1.pop(b, c.pop())
where you are updating the dictionary "dict1" with the key "b" and the value c.pop(), (last value of the list in c, equivalent to c[-1] in this case). Note that this is possible because the key value b=7 is not in you original dictionary.
Related
How can I create a key:value pair in a first loop and then just append values in subsequent loops?
For example:
a = [1,2,3]
b = [8,9,10]
c = [4,6,5]
myList= [a,b,c]
positions= ['first_position', 'second_position', 'third_position']
I would like to create a dictionary which records the position values for each letter so:
mydict = {'first_position':[1,8,4], 'second_position':[2,9,6], 'third_position':[3,10,5]}
Imagine that instead of 3 letters with 3 values each, I had millions. How could I loop through each letter and:
In the first loop create the key:value pair 'first_position':[1]
In subsequent loops append values to the corresponding key: 'first_position':[1,8,4]
Thanks!
Try this code:
mydict = {}
for i in range(len(positions)):
mydict[positions[i]] = [each[i] for each in myList]
Output:
{'first_position': [1, 8, 4],
'second_position': [2, 9, 6],
'third_position': [3, 10, 5]}
dictionary.get('key') will return None if the key doesn't exist. So, you can check if the value is None and then append it if it isn't.
dict = {}
for list in myList:
for position, val in enumerate(list):
this_position = positions[position]
if dict.get(this_position) is not None:
dict[this_position].append(val)
else:
dict[this_position] = [val]
The zip function will iterate the i'th values of positions, a, b and c in order. So,
a = [1,2,3]
b = [8,9,10]
c = [4,6,5]
positions= ['first_position', 'second_position', 'third_position']
sources = [positions, a, b, c]
mydict = {vals[0]:vals[1:] for vals in zip(*sources)}
print(mydict)
This created tuples which is usually fine if the lists are read only. Otherwise do
mydict = {vals[0]:list(vals[1:]) for vals in zip(*sources)}
I need to compare dictionary b against a to check whether the keys of b are in a.
If present check the values of a[key]==b[key]. If not equal print the key:value pair of both dictionaries for reference. How can I do that?
a = {'key_1': 1,'key_2': 2, 'key_3': 3}
b = {'key_1': 1,'key_2': 5}
[k for key in b if key in a if b[k]!=a[k]]
I used the above code, but not able to print both the dictionaries keys and value as like
not equal: b[key_2]=5 and a[key_2]=2
I need to compare dictionary b against a to check whether the keys of b are in a. You want to find the intersecting keys and then check their values:
a = {'key_1': 1,'key_2': 2, 'key_3': 3}
b = {'key_1': 1,'key_2': 5}
# find keys common to both
inter = a.keys() & b
diff_vals = [(k, a[k], b[k]) for k in inter if a[k] != b[k]]
# find keys common to both
inter = a.keys() & b
for k,av, bv in diff_vals:
print("key = {}, val_a = {}, val_b = {}".format(k, av, bv))
key = key_2, val_a = 2, val_b = 5
You can use many different set methods on the dict_view objetcs:
# find key/value pairings that are unique to either dict
symmetric = a.items() ^ b.items()
{('key_2', 2), ('key_2', 5), ('key_3', 3)}
# key/values in b that are not in a
difference = b.items() - a.items()
{('key_2', 5)}
# key/values in a that are not in b
difference = a.items() - b.items()
{('key_3', 3), ('key_2', 2)}
# get unique set of all keys from a and b
union = a.keys() | b
{'key_1', 'key_2', 'key_3'}
# get keys common to both dicts
inter = a.keys() & b
{'key_1', 'key_2'}
What you want is likely this:
result = [(k, a[k], b[k]) for k in a if k in b and a[k]!=b[k]]
In other words, "generate a list of tuples composed of the key, the first value and the second, whenever a key in a is also in b and the corresponding values are not equal".
Since the boolean expressions with "and" are failsafe (they evaluate from left to right and stop as soon as a False value is found), you don't have to worry that "b[k]!=a[k]" could raise an exception.
This raises another question: what if the key is in a and not b or vice-versa, e.g. ('car', 2, None) or ('car', None, 2)? Should that also be a valid answer?
I think there is a small error in the code you posted. First, you seem to mix k and key for the same object. Second, you cannot have two if-clauses in a list comprehension, but instead you can combine them with and. Here is how it could look: [k for k in b if k in a and a[k]!=b[k]]
This will produce a list with all the keys for which the values don't match. Given such a key, you can simply use e.g. "a[{k}]={a} and b[{k}]={b}".format(k=k,a=a[k],b=b[k]) to get a human-readable string describing that mismatch. Of couse, if you are only going to create that list of keys to loop over it afterwards (for printing), then there is no need to actually create that list in the first place. Simply iterate directly over the dictionary keys.
This might work
a = {'key_1': 1,'key_2': 2, 'key_3': 3}
b = {'key_1': 1,'key_2': 5}
i=[k for k in b if k in a if b[k]!=a[k]]
if i:
for k in i:
print('not equal:b[',k,']=',b[k],'and a[',k,']=',a[k])
Output
not equal:b[ key_2 ]= 5 and a[ key_2 ]= 2
I have made a small demo of a more complex problem
def f(a):
return tuple([x for x in range(a)])
d = {}
[d['1'],d['2']] = f(2)
print d
# {'1': 0, '2': 1}
# Works
Now suppose the keys are programmatically generated
How do i achieve the same thing for this case?
n = 10
l = [x for x in range(n)]
[d[x] for x in l] = f(n)
print d
# SyntaxError: can't assign to list comprehension
You can't, it's a syntactical feature of the assignment statement. If you do something dynamic, it'll use different syntax, and thus not work.
If you have some function results f() and a list of keys keys, you can use zip to create an iterable of keys and results, and loop over them:
d = {}
for key, value in zip(keys, f()):
d[key] = value
That is easily rewritten as a dict comprehension:
d = {key: value for key, value in zip(keys, f())}
Or, in this specific case as mentioned by #JonClements, even as
d = dict(zip(keys, f()))
Let's say I have a regular "dict-of-dicts" as follows:
d = {}
d['a'] = {}
d['a']['b'] = 3
I can of course access the element using d['a']['b'].
In my case, I have a recursive application, in which I keep the current state as a list of keys. So I would have
my_key = ['a', 'b']
How do I access the value 3, using my_key? The issue, of course, is that my_key can be arbitrarily long (deep).
I realize I can write another traversal function or so, but it seems like there should be a straightforward way of doing so. Any ideas?
You could use reduce to iteratively index each layer of dict with a different key:
>>> from functools import reduce #only necessary in 3.X
>>> d = {}
>>> d['a'] = {} #I'm assuming this is what you meant to type
>>> d['a']['b'] = 3
>>> keys = ("a", "b")
>>> reduce(dict.get, keys, d)
3
Currently dictionary keys can only be hashable types, list (ListType) is not one of them, so if you try to specify a list as a dictionary key:
{}[[]]
you'll get:
TypeError: unhashable type: 'list'`.
You could enhance the current dictionary, allowing to specify a list as a key, and iterate over the list on inner objects. Here's the code (note that it only takes care of the get/read part):
from types import DictType, ListType
class EnhancedDictType(DictType):
def __getitem__(self, key):
if key and isinstance(key, ListType):
new_obj = self
for item in key:
new_obj = new_obj[item]
return new_obj
else:
return super(EnhancedDictType, self).__getitem__(key)
dict = EnhancedDictType
Here's also some test code:
d = dict()
d[1] = dict()
d[1][2] = dict({3: 4})
d[(1, 2, 3)] = 5
print d
print d[1]
print d[1][2]
print d[[1, 2]]
print d[[1 ,2, 3]]
print d[(1, 2, 3)]
I am trying to 'destructure' a dictionary and associate values with variables names after its keys. Something like
params = {'a':1,'b':2}
a,b = params.values()
But since dictionaries are not ordered, there is no guarantee that params.values() will return values in the order of (a, b). Is there a nice way to do this?
from operator import itemgetter
params = {'a': 1, 'b': 2}
a, b = itemgetter('a', 'b')(params)
Instead of elaborate lambda functions or dictionary comprehension, may as well use a built in library.
One way to do this with less repetition than Jochen's suggestion is with a helper function. This gives the flexibility to list your variable names in any order and only destructure a subset of what is in the dict:
pluck = lambda dict, *args: (dict[arg] for arg in args)
things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')
Also, instead of joaquin's OrderedDict you could sort the keys and get the values. The only catches are you need to specify your variable names in alphabetical order and destructure everything in the dict:
sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))
things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)
How come nobody posted the simplest approach?
params = {'a':1,'b':2}
a, b = params['a'], params['b']
Python is only able to "destructure" sequences, not dictionaries. So, to write what you want, you will have to map the needed entries to a proper sequence. As of myself, the closest match I could find is the (not very sexy):
a,b = [d[k] for k in ('a','b')]
This works with generators too:
a,b = (d[k] for k in ('a','b'))
Here is a full example:
>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2
Here's another way to do it similarly to how a destructuring assignment works in JS:
params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
What we did was to unpack the params dictionary into key values (using **) (like in Jochen's answer), then we've taken those values in the lambda signature and assigned them according to the key name - and here's a bonus - we also get a dictionary of whatever is not in the lambda's signature so if you had:
params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)
After the lambda has been applied, the rest variable will now contain:
{'c': 3}
Useful for omitting unneeded keys from a dictionary.
Hope this helps.
Maybe you really want to do something like this?
def some_func(a, b):
print a,b
params = {'a':1,'b':2}
some_func(**params) # equiv to some_func(a=1, b=2)
If you are afraid of the issues involved in the use of the locals dictionary and you prefer to follow your original strategy, Ordered Dictionaries from python 2.7 and 3.1 collections.OrderedDicts allows you to recover you dictionary items in the order in which they were first inserted
(Ab)using the import system
The from ... import statement lets us desctructure and bind attribute names of an object. Of course, it only works for objects in the sys.modules dictionary, so one could use a hack like this:
import sys, types
mydict = {'a':1,'b':2}
sys.modules["mydict"] = types.SimpleNamespace(**mydict)
from mydict import a, b
A somewhat more serious hack would be to write a context manager to load and unload the module:
with obj_as_module(mydict, "mydict_module"):
from mydict_module import a, b
By pointing the __getattr__ method of the module directly to the __getitem__ method of the dict, the context manager can also avoid using SimpleNamespace(**mydict).
See this answer for an implementation and some extensions of the idea.
One can also temporarily replace the entire sys.modules dict with the dict of interest, and do import a, b without from.
Warning 1: as stated in the docs, this is not guaranteed to work on all Python implementations:
CPython implementation detail: This function relies on Python stack frame support
in the interpreter, which isn’t guaranteed to exist in all implementations
of Python. If running in an implementation without Python stack frame support
this function returns None.
Warning 2: this function does make the code shorter, but it probably contradicts the Python philosophy of being as explicit as you can. Moreover, it doesn't address the issues pointed out by John Christopher Jones in the comments, although you could make a similar function that works with attributes instead of keys. This is just a demonstration that you can do that if you really want to!
def destructure(dict_):
if not isinstance(dict_, dict):
raise TypeError(f"{dict_} is not a dict")
# the parent frame will contain the information about
# the current line
parent_frame = inspect.currentframe().f_back
# so we extract that line (by default the code context
# only contains the current line)
(line,) = inspect.getframeinfo(parent_frame).code_context
# "hello, key = destructure(my_dict)"
# -> ("hello, key ", "=", " destructure(my_dict)")
lvalues, _equals, _rvalue = line.strip().partition("=")
# -> ["hello", "key"]
keys = [s.strip() for s in lvalues.split(",") if s.strip()]
if missing := [key for key in keys if key not in dict_]:
raise KeyError(*missing)
for key in keys:
yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}
In [6]: hello, key = destructure(my_dict)
In [7]: hello
Out[7]: 'world'
In [8]: key
Out[8]: 'value'
This solution allows you to pick some of the keys, not all, like in JavaScript. It's also safe for user-provided dictionaries
With Python 3.10, you can do:
d = {"a": 1, "b": 2}
match d:
case {"a": a, "b": b}:
print(f"A is {a} and b is {b}")
but it adds two extra levels of indentation, and you still have to repeat the key names.
Look for other answers as this won't cater to the unexpected order in the dictionary. will update this with a correct version sometime soon.
try this
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
keys = data.keys()
a,b,c = [data[k] for k in keys]
result:
a == 'Apple'
b == 'Banana'
c == 'Carrot'
Well, if you want these in a class you can always do this:
class AttributeDict(dict):
def __init__(self, *args, **kwargs):
super(AttributeDict, self).__init__(*args, **kwargs)
self.__dict__.update(self)
d = AttributeDict(a=1, b=2)
Based on #ShawnFumo answer I came up with this:
def destruct(dict): return (t[1] for t in sorted(dict.items()))
d = {'b': 'Banana', 'c': 'Carrot', 'a': 'Apple' }
a, b, c = destruct(d)
(Notice the order of items in dict)
An old topic, but I found this to be a useful method:
data = {'a':'Apple', 'b':'Banana','c':'Carrot'}
for key in data.keys():
locals()[key] = data[key]
This method loops over every key in your dictionary and sets a variable to that name and then assigns the value from the associated key to this new variable.
Testing:
print(a)
print(b)
print(c)
Output
Apple
Banana
Carrot
An easy and simple way to destruct dict in python:
params = {"a": 1, "b": 2}
a, b = [params[key] for key in ("a", "b")]
print(a, b)
# Output:
# 1 2
I don't know whether it's good style, but
locals().update(params)
will do the trick. You then have a, b and whatever was in your params dict available as corresponding local variables.
Since dictionaries are guaranteed to keep their insertion order in Python >= 3.7, that means that it's complete safe and idiomatic to just do this nowadays:
params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)
Output:
1
2