I'd like to create a dict in a list comprehension that might not have some key. So far, I came up with this, but it looks rather ugly
{
"foo": 1,
<more fields>,
**({"bar": 2} if bar else {})
}
Alternatively,
dict(
foo=1,
<more fields>,
**({"bar": 2} if bar else {})
)
Is there a cleaner way to do this? I'm looking for an expression for a dict where some keys might not be present based on a condition.
Related
I want to solve the following problem without introducing several if/else if cases. I have the following dictionaries:
dict_1 = {"full_name": {"name": "Foo", "surname": "Bar"}, "age": 29, "test": 0}
dict_2 = {"name": "Foo", "age": 29, "test": 1}
Now, I want to receive the value of the key "name" for both dictionaries. In the "real" program situation, I don't know if a structure of dict_1 or dict_2 will be present, but in all structures that will be present, the key "name" will exist. So I need a function like the following:
filter_dict_for_key(dict, key):
...
return val(key)
For both dictionaries, I expect the output "Foo".
Is there an elegant pythonic way to do this with filter/dict comprehension, etc.?
A recursive function is usually the easiest way to handle an arbitrarily nested object. Since there's always the possibility of there being more (or fewer) than one matching item, this function returns a list of matches:
>>> def get_name_nested(d):
... """Return a list of all 'name' values in a nested dict."""
... if not isinstance(d, dict):
... return []
... if 'name' in d:
... return [d['name']]
... return [n for s in d.values() for n in get_name_nested(s)]
...
>>> get_name_nested({"full_name": {"name": "Foo", "surname": "Bar"}, "age": 29, "test": 0})
['Foo']
>>> get_name_nested({"name": "Foo", "age": 29, "test": 1})
['Foo']
If you need to handle additional types of nesting (e.g. a list of dicts or a list of lists which may themselves be nested), it's pretty straightforward to extend this approach to handle those.
You can use the default argument to dict.get()
name = mydict.get('name', mydict.get('full_name', {}).get('name'))
So if there's no name key in the top-level, it looks in the full_name key. You have to use .get() for the default in case there's no full_name key.
Or use a conditional expression:
name = mydict['name'] if 'name' in mydict else mydict['full_name']['name']
Note that this will get an error if neither key exists. You can use try/except to catch that if necessary.
I am looking to put 2 dictionaries into one dictionary.
note. I am not looking to merge them or join them. I am looking for a method similar append for dictionary.
Note;
I want them to hold their structures. don't want the data to mix up
Example
I Have this,
dic1 = {'Apple':'Mac', 'Microsoft': 'Surface', 'Google': 'Chromebook', 'Lenovo':'ThinkPad'}
dic2 = {1:'Apple', 2:'Amazon', 3:'Microsoft', 4:'Google', 5:'Facebook'}
I want this
dic3 = {{'Apple':'Mac', 'Microsoft': 'Surface', 'Google': 'Chromebook', 'Lenovo':'ThinkPad'},
{1:'Apple', 2:'Amazon', 3:'Microsoft', 4:'Google', 5:'Facebook'}}
Thanks in advance
That's not how dictionaries work. You can have a list or tuple (or possibly a frozenset) that looks mostly like what you want, but dictionaries do not allow this kind of linear concatenation. They are strictly associative containers, meaning that every value must have an associated key.
If you can associate each dictionary with a key, however, then what you want becomes extremely straightforward:
dic3 = {
'dic1': {'Apple':'Mac', 'Microsoft': 'Surface', 'Google': 'Chromebook', 'Lenovo':'ThinkPad'},
'dic2': {1:'Apple', 2:'Amazon', 3:'Microsoft', 4:'Google', 5:'Facebook'}
}
Or, using your variables:
dic3 = {'dic1': dic1, 'dic2': dic2}
Update: check the added example at the bottom.
If you don't want to change dic1 or dic2, you can do this:
dic3 = {**dic1, **dic2}
dic3 will be a dictionary.
Any common key will have the value appearing later (to the right).
For the type of output you expect (set of dictionaries), you need to create a hashable dictionary as follows:
class hashabledict(dict):
def __hash__(self):
return hash(tuple(sorted(self.items())))
dic1 = {'Apple':'Mac', 'Microsoft': 'Surface', 'Google': 'Chromebook', 'Lenovo':'ThinkPad'}
dic2 = {1:'Apple', 2:'Amazon', 3:'Microsoft', 4:'Google', 5:'Facebook'}
dic3 = {hashabledict(dic1), hashabledict(dic2)}
The above hashabledict class definition has been taken from here: Python hashable dicts
If I have a dictionary that is nested, and I pass in a string like "key1.key2.key3" which would translate to:
myDict["key1"]["key2"]["key3"]
What would be an elegant way to be able to have a method where I could pass on that string and it would translate to that key assignment? Something like
myDict.set_nested('key1.key2.key3', someValue)
Using only builtin stuff:
def set(my_dict, key_string, value):
"""Given `foo`, 'key1.key2.key3', 'something', set foo['key1']['key2']['key3'] = 'something'"""
# Start off pointing at the original dictionary that was passed in.
here = my_dict
# Turn the string of key names into a list of strings.
keys = key_string.split(".")
# For every key *before* the last one, we concentrate on navigating through the dictionary.
for key in keys[:-1]:
# Try to find here[key]. If it doesn't exist, create it with an empty dictionary. Then,
# update our `here` pointer to refer to the thing we just found (or created).
here = here.setdefault(key, {})
# Finally, set the final key to the given value
here[keys[-1]] = value
myDict = {}
set(myDict, "key1.key2.key3", "some_value")
assert myDict == {"key1": {"key2": {"key3": "some_value"}}}
This traverses myDict one key at a time, ensuring that each sub-key refers to a nested dictionary.
You could also solve this recursively, but then you risk RecursionError exceptions without any real benefit.
There are a number of existing modules that will already do this, or something very much like it. For example, the jmespath module will resolve jmespath expressions, so given:
>>> mydict={'key1': {'key2': {'key3': 'value'}}}
You can run:
>>> import jmespath
>>> jmespath.search('key1.key2.key3', mydict)
'value'
The jsonpointer module does something similar, although it likes / for a separator instead of ..
Given the number of pre-existing modules I would avoid trying to write your own code to do this.
EDIT: OP's clarification makes it clear that this answer isn't what he's looking for. I'm leaving it up here for people who find it by title.
I implemented a class that did this a while back... it should serve your purposes.
I achieved this by overriding the default getattr/setattr functions for an object.
Check it out! AndroxxTraxxon/cfgutils
This lets you do some code like the following...
from cfgutils import obj
a = obj({
"b": 123,
"c": "apple",
"d": {
"e": "nested dictionary value"
}
})
print(a.d.e)
>>> nested dictionary value
I'm looking for a way to name a value within an expression to use it multiple times within that expression. Since the value is found inside the expression, I can't save it as a variable using a typical assign statement. I also want its use to be in the same function as the rest of the expression, so I would rather not break it out into a separate function.
More specifically, I enjoy comprehension. List/dictionary comprehension is my favorite Python feature. I'm trying to use both to coerce a dictionary of untrusted structure into a trusted structure (all fields exist, and their values are of the correct type). Without what I'm looking for, it would look something like this:
{
...
'outer': [{
...
'inner': {
key: {
...
'foo': {
'a': get_foo_from_value(value)['a'],
'b': get_foo_from_value(value)['b'],
...
}
} for key, value in get_inner_from_outer(outer)
}
} for outer in get_outer_from_dictionary(dictionary)]
}
Those function calls are actually expressions, but I would like to only evaluate get_foo_from_value(value) once. Ideally there would be something like this:
'foo': {
'a': foo['a'],
'b': foo['b'],
...
} with get_foo_from_value(value) as foo
So far the options I've come up with are single-item generators and lambda expressions. I'm going to include an example of each as an answer so they can be discussed separately.
lambda solution
'foo': (lambda foo: {
'a': foo['a'],
'b': foo['b'],
...
})(get_foo_from_value(value))
I feel like this one isn't as readable as it could be. I also don't like creating a lambda that only gets called once. I like that the name appears before it's used, but I don't like the separation of its name and value.
single-item generator solution
This is currently my favorite solution to the problem (I like comprehension, remember).
'foo': next({
'a': foo['a'],
'b': foo['b'],
...
} for foo in [get_foo_from_value(value)])
I like it because the generator expression matches the rest of the comprehension in the expression, but I'm not a huge fan of the next and having to wrap get_foo_from_value(value) in brackets.
How do I change all dots . to underscores (in the dict's keys), given an arbitrarily nested dictionary?
What I tried is write two loops, but then I would be limited to 2-level-nested dictionaries.
This ...
{
"brown.muffins": 5,
"green.pear": 4,
"delicious.apples": {
"green.apples": 2
{
}
... should become:
{
"brown_muffins": 5,
"green_pear": 4,
"delicious_apples": {
"green_apples": 2
{
}
Is there an elegant way?
You can write a recursive function, like this
from collections.abc import Mapping
def rec_key_replace(obj):
if isinstance(obj, Mapping):
return {key.replace('.', '_'): rec_key_replace(val) for key, val in obj.items()}
return obj
and when you invoke this with the dictionary you have shown in the question, you will get a new dictionary, with the dots in keys replaced with _s
{'delicious_apples': {'green_apples': 2}, 'green_pear': 4, 'brown_muffins': 5}
Explanation
Here, we just check if the current object is an instance of dict and if it is, then we iterate the dictionary, replace the key and call the function recursively. If it is actually not a dictionary, then return it as it is.
Assuming . is only present in keys and all the dictionary's contents are primitive literals, the really cheap way would be to use str() or repr(), do the replacement, then ast.literal_eval() to get it back:
d ={
"brown.muffins": 5,
"green.pear": 4,
"delicious_apples": {
"green.apples": 2
} # correct brace
}
Result:
>>> import ast
>>> ast.literal_eval(repr(d).replace('.','_'))
{'delicious_apples': {'green_apples': 2}, 'green_pear': 4, 'brown_muffins': 5}
If the dictionary has . outside of keys, we can replace more carefully by using a regular expression to look for strings like 'ke.y': and replace only those bits:
>>> import re
>>> ast.literal_eval(re.sub(r"'(.*?)':", lambda x: x.group(0).replace('.','_'), repr(d)))
{'delicious_apples': {'green_apples': 2}, 'green_pear': 4, 'brown_muffins': 5}
If your dictionary is very complex, with '.' in values and dictionary-like strings and so on, use a real recursive approach. Like I said at the start, though, this is the cheap way.