Split on whitespaces in keys of dict, take value - python

I have a dictionary which has this form:
myDict = {'foo': bar, 'foobar baz': qux}
Now, I want to split on whitespaces in the key of the dict, make it the next key and take the value (duplicate).
myDictRev1 = {'foo': bar, 'foobar': qux, 'baz': qux}

You can use dictionary comprehensions like this:
>>> myDict = {'foo': 'bar', 'foobar baz': 'qux'}
>>> {k:v for k, v in myDict.items() for k in k.split()}
{'baz': 'qux', 'foo': 'bar', 'foobar': 'qux'}

Related

simplify code using an inverse dictionary in python

Consider some mapping my_map that defines the order of some keys, and some dictionary my_dict that maps the same keys into some values:
my_map = {'x' : 2, 'y' : 0, 'z' : 1}
my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
I want to get an ordered list of the values of my_dict using the order defined by my_map. My best approach of getting there is:
inv_map = {v: k for k, v in my_map.items()}
ordered_list = [my_dict[k] for k in [inv_map[d] for d in range(len(my_map))]]
Is there a less clunky way of doing the same?
You could use the sorted function to order your map by value and then convert it:
[my_dict[k] for k in sorted(my_map, key=lambda key: my_map[key])]
Somewhat cleaner at least!
Let's make sure that it works:
>>> my_map = {'x' : 2, 'y' : 0, 'z' : 1}
>>> my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
>>> [my_dict[k] for k in sorted(my_map, key=lambda key: my_map[key])]
['baz', 'bar', 'foo']
You can actually use sorted very efficiently here using dict.get:
[my_dict[k] for k in sorted(my_map, key=my_map.get)]
In action:
>>> my_map = {'x' : 2, 'y' : 0, 'z' : 1}
>>> my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
>>> [my_dict[k] for k in sorted(my_map, key=my_map.get)]
['baz', 'bar', 'foo']
Depends on the situation you can init final list and pass items to the needed positions
result = [None] * len(my_dict)
for k, v in my_dict.items():
result[my_map[k]] = v
Still another variation (I think it's different from already presented solutions):
[x[1] for x in sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])]
Testing code:
my_map = {'x' : 2, 'y' : 0, 'z' : 1}
my_dict = {'x' : 'foo', 'z' : 'bar', 'y' : 'baz'}
print(my_dict.items())
sorted_result=[x[1] for x in sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])]
print(sorted_result)
Or a bit differently:
sorted_result=list(zip(*sorted(my_dict.items(), key=lambda elem: my_map[elem[0]])))[1]
I wanted to use zip() to split a list of tuples into 2 lists, but in Python 3 zip() returns iterator (not a list), so (as suggested in Transpose/Unzip Function (inverse of zip)?) I wrapped it in list()
you can use sorted with the values from my_dict and a key function that sorts them with the values from my_map
ordered_list = sorted(my_dict.values(), key=lambda s:my_map[{v: k for k, v in my_dict.items()}[s]])
if it's sorted the wrong way you can use reverse=True

Edit dictionary values if keys found in list

Is there a cleaner way to do this? Could I accomplish this via list comprehension?
data = {'foo': 'bar', 'twofoo': 'twobar', 'threefoo': 'threebar'}
some_list = ['foo', 'threefoo']
for key in data:
for item in some_list:
if key==item:
data[key] = 'randomfoo'
I want to clean the values from a dictionary
so 'random foo' will always be the same
Care to elaborate on the downvotes?
You can take the intersection of the dictionary keys with the list, and update values at those keys:
for key in set(data).intersection(some_list):
data[key] = 'foobar'
print(data)
# {'twofoo': 'twobar', 'threefoo': 'foobar', 'foo': 'foobar'}
You could also define your list as a set and do a quick membership check (with O(1) complexity) for each key:
some_set = {'foo', 'threefoo'}
for key in data:
if key in some_set:
data[key] = 'randomfoo'
Using dict comprehension:
res = {key: 'randomfoo' if key in some_list else value for key, value in data.items()}
Output:
>>> res
{'twofoo': 'twobar', 'threefoo': 'randomfoo', 'foo': 'randomfoo'}
You can try this way :
data = {'foo': 'bar', 'twofoo': 'twobar', 'threefoo': 'threebar'}
some_list = ['foo', 'threefoo']
for key in data:
if key in some_list:
data[key] = "randomfoo"
print(data)
Output:
{'foo': 'randomfoo', 'twofoo': 'twobar', 'threefoo': 'randomfoo'}

recursive access to dictionary and modification

I have the following dictionary:
my_dict = {'key1': {'key2': {'foo': 'bar'} } }
and I would like to append an entry to key1->key2->key3 with value 'blah' yielding:
my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': 'blah'} } }
I am looking for a generic solution that is independent of the number of keys, i.e. key1->key2->key3->key4->key5 should work as well, even though keys from key3 on downwards do not exist. So that I get:
my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'} } } } }
Thanks in advance.
You can use the reduce() function to traverse a series of nested dictionaries:
def get_nested(d, path):
return reduce(dict.__getitem__, path, d)
Demo:
>>> def get_nested(d, path):
... return reduce(dict.__getitem__, path, d)
...
>>> my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'}}}}}
>>> get_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'))
'blah'
This version throws an exception when a key doesn't exist:
>>> get_nested(my_dict, ('key1', 'nonesuch'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in get_nested
KeyError: 'nonesuch'
but you could replace dict.__getitem__ with lambda d, k: d.setdefault(k, {}) to have it create empty dictionaries instead:
def get_nested_default(d, path):
return reduce(lambda d, k: d.setdefault(k, {}), path, d)
Demo:
>>> def get_nested_default(d, path):
... return reduce(lambda d, k: d.setdefault(k, {}), path, d)
...
>>> get_nested_default(my_dict, ('key1', 'nonesuch'))
{}
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}, 'nonesuch': {}}}
To set a value at a given path, traverse over all keys but the last one, then use the final key in a regular dictionary assignment:
def set_nested(d, path, value):
get_nested_default(d, path[:-1])[path[-1]] = value
This uses the get_nested_default() function to add empty dictionaries as needed:
>>> def set_nested(d, path, value):
... get_nested_default(d, path[:-1])[path[-1]] = value
...
>>> my_dict = {'key1': {'key2': {'foo': 'bar'}}}
>>> set_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'), 'blah')
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}}}
An alternative to Martijn Pieters's excellent answer would be to use a nested defaultdict, rather than a regular dictionary:
from collections import defaultdict
nested = lambda: defaultdict(nested) # nested dictionary factory
my_dict = nested()
You can set values by using regular nested dictionary access semantics, and empty dictionaries will be created to fill the middle levels as necessary:
my_dict["key1"]["key2"]["key3"] = "blah"
This of course requires that the number of keys be known in advance when you write the code to set the value. If you want to be able to handle a variable-length list of keys, rather than a fixed number, you'll need functions to do the getting and setting for you, like in Martijn's answer.

Combine two dictionaries, concatenate string values?

Related: Is there any pythonic way to combine two dicts (adding values for keys that appear in both)?
I'd like to merge two string:string dictionaries, and concatenate the values. The above post recommends using collections.Counter, but it doesn't handle string concatenation.
>>> from collections import Counter
>>> a = Counter({'foo':'bar', 'baz':'bazbaz'})
>>> b = Counter({'foo':'baz'})
>>> a + b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/collections.py", line 569, in __add__
TypeError: cannot concatenate 'str' and 'int' objects
(My guess is Counter tries to set b['baz'] to 0.)
I'd like to get a result of {'foo':'barbaz', 'baz':'bazbaz'}. Concatenation order doesn't matter to me. What is a clean, Pythonic way to do this?
Dict-comprehension:
>>> d = {'foo': 'bar', 'baz': 'bazbaz'}
>>> d1 = {'foo': 'baz'}
>>> keys = d.viewkeys() | d1.viewkeys()
>>> {k : d.get(k, '') + d1.get(k, '') for k in keys}
{'foo': 'barbaz', 'baz': 'bazbaz'}
For Python 2.6 and earlier:
>>> dict((k, d.get(k, '') + d1.get(k, '')) for k in keys)
{'foo': 'barbaz', 'baz': 'bazbaz'}
This will work for any number of dicts:
def func(*dicts):
keys = set().union(*dicts)
return {k: "".join(dic.get(k, '') for dic in dicts) for k in keys}
...
>>> d = {'foo': 'bar', 'baz': 'bazbaz'}
>>> d1 = {'foo': 'baz','spam': 'eggs'}
>>> d2 = {'foo': 'foofoo', 'spam': 'bar'}
>>> func(d, d1, d2)
{'foo': 'barbazfoofoo', 'baz': 'bazbaz', 'spam': 'eggsbar'}
Can write a generic helper, such as:
a = {'foo':'bar', 'baz':'bazbaz'}
b = {'foo':'baz'}
def concatd(*dicts):
if not dicts:
return {} # or should this be None or an exception?
fst = dicts[0]
return {k: ''.join(d.get(k, '') for d in dicts) for k in fst}
print concatd(a, b)
# {'foo': 'barbaz', 'baz': 'bazbaz'}
c = {'foo': '**not more foo!**'}
print concatd(a, b, c)
# {'foo': 'barbaz**not more foo!**', 'baz': 'bazbaz'}
One can use defaultdict to achieve this:
from collections import defaultdict
a = {'foo': 'bar', 'baz': 'bazbaz'}
b = {'foo': 'baz'}
new_dict = defaultdict(str)
for key, value in a.items():
new_dict[key] += value
for key, value in b.items():
new_dict[key] += value
print(new_dict)
# defaultdict(<class 'str'>, {'foo': 'barbaz', 'baz': 'bazbaz'})
print(dict(new_dict))
# {'foo': 'barbaz', 'baz': 'bazbaz'}
If there are many dicts to join, we could use itertools.chain.from_iterable:
from collections import defaultdict
from itertools import chain
a = {'foo': 'bar', 'baz': 'bazbaz'}
b = {'foo': 'baz'}
c = {'baz': '123'}
dicts = [a, b, c]
new_dict = defaultdict(str)
for key, value in chain.from_iterable(map(dict.items, dicts)):
new_dict[key] += value
print(dict(new_dict))
# {'foo': 'barbaz', 'baz': 'bazbaz123'}

Sort a list of Python dictionaries depending on a ordered criteria

Hi, I want to order a list of dictionaries based on an ordered criteria in the most Pythonic way. For example:
[{'foo': FOO1}, {'foo': FOO2}, {'foo': FOO10}]
The criteria is variable, for example, I want to order first by [FOO2, FOO1, FOO8, FOO10], the result would be:
[{'foo': FOO2}, {'foo': FOO1}, {'foo': FOO10}]
Then, the situation changes and now we have another criteria [FOO2, FOO10, FOO1], the result would be:
[{'foo': FOO2}, {'foo': FOO10}, {'foo': FOO1}]
Note: The criteria will always have the symbols related to key 'foo'.
Any ideas?
to_sort = [{'foo': FOO1}, {'foo': FOO2}, {'foo': FOO10}]
to_sort.sort(key=lambda x: x....)
EDIT: I figured it out:
>>> to_sort = [{'foo': FOO1}, {'foo': FOO2}, {'foo': FOO10}]
>>> criteria = [FOO10, FOO2, FOO1]
>>> to_sort.sort(key=lambda x: criteria.index(x['foo']))
>>> to_sort
[{'foo': FOO10}, {'foo': FOO2}, {'foo': FOO1}]
Kind regards
You want list.index().

Categories