Often when I'm working with dictionaries whose values are lists, I happen to code this:
if k in D:
D[k].append(x)
else:
D[k] = [x]
Is there a more concise way of doing this?
You can use a collections.defaultdict:
from collections import defaultdict
D = defaultdict(list)
D[k].append(x)
The most concise way is to use setdefault like:
D.setdefault(k, []).append(x)
Related
I have a dictionary like this:
dict_str = {'Number_1_item':'foo',
'Number_11_item':'bar',
'Number_22_item':'foobar',
'Number_5_item':'barfoo'}
And my desired out put is:
sorted_dict_str = {'Number_1_item':'foo',
'Number_5_item':'bar',
'Number_11_item':'foobar',
'Number_22_item':'barfoo'}
So the sorted_dict_str is sorted in numerical way based on the keys in dict_str.
I have found some answers to sort the keys with pure numeric keys but not mixed ones, so they are not really helpful.
Thanks a lot.
Regards
You can get sorted dict from your dict like that:
from collections import OrderedDict
OrderedDict(sorted(dict_str.items(), key=lambda s: int(s[0].split('_')[1])))
If we can assume that all the keys are on the format Number_XX_item, you could simply sort on the numeric part, using a lambda:
sorted_dict_str = sorted(dict_str.items(), key=lambda x: int(x[0].split('_')[1]))
This gives the following output:
sorted_dict_str =
[('Number_1_item', 'foo'),
('Number_5_item', 'barfoo'),
('Number_11_item', 'bar'),
('Number_22_item', 'foobar')]
Yet another solution. When executing the sorted function it'll return a sorted list of all the keys in the dictionary. A dictionary can't be sorted (though there is a way of having an ordered dictionary).
This Solution is also more robust compared to the first solution, since the number can be anywhere in the key.
import re
from collections import OrderedDict
d = {'Number_1_item':'foo',
'Number_11_item':'bar',
'Number_22_item':'foobar',
'Number_5_item':'barfoo'}
keys = d.keys()
def sort_mixed(key):
int_match = re.search(r'(?P<int>\d+)', key)
number = int_match.group(0)
return int(number)
sorted_keys = sorted(keys, key=lambda key: sort_mixed(key))
print(sorted_keys) # print sorted keys
sorted_dict = OrderedDict((k, d[k]) for k in sorted_keys)
print(sorted_dict) # print new sorted dict
You should check out natsort. There are ways to do it by yourself, without importing extra modules, but I like is method.
>>> from collections import OrderedDict
>>> import natsort
>>> input_dict = {'Number_1_item':'foo', 'Number_11_item':'bar', 'Number_22_item':'foobar', 'Number_5_item':'barfoo'}
>>> OrderedDict(natsort.natsorted(input_dict.items()))
OrderedDict([('Number_1_item', 'foo'), ('Number_5_item', 'barfoo'), ('Number_11_item', 'bar'), ('Number_22_item', 'foobar')])
Here's a solution using the slicing technique I mentioned in the comments. This is less robust than using .split, since the lengths of the prefix & suffix strings are hard-coded, but it's slightly faster because slicing is fast compared to a method call.
from collections import OrderedDict
from pprint import pprint
dict_str = {
'Number_1_item':'foo',
'Number_11_item':'bar',
'Number_22_item':'foobar',
'Number_5_item':'barfoo',
}
skeys = sorted(dict_str.keys(), key=lambda s: int(s[7:][:-5]))
sorted_dict = OrderedDict((k, dict_str[k]) for k in skeys)
pprint(sorted_dict)
output
OrderedDict([('Number_1_item', 'foo'),
('Number_5_item', 'barfoo'),
('Number_11_item', 'bar'),
('Number_22_item', 'foobar')])
You could also do the sorting on the (key, value) pairs inside the OrderedDict constructor call:
sorted_dict = OrderedDict(sorted(dict_str.items(), key=lambda u: int(u[0][7:][:-5])))
but I think my previous version is a little more readable.
You can sort the keys of a dictionary into a list, with the additional key-Argument:
dict_str = {'Number_1_item':'foo',
'Number_11_item':'bar',
'Number_22_item':'foobar',
'Number_5_item':'barfoo'}
sorted_keys = sorted(dict_str, key=lambda x: int(x.split('_')[1]))
You can't sort the dict itself by definition of what a dict is.
But you can sort its keys in any custom order by passing the desired 'key' argument into sorted function
sorted(iterable[, cmp[, key[, reverse]]])
https://docs.python.org/2/library/functions.html#sorted
I need to create a structure like this :
D = {i:{j:{k:0,l:1,m:2}},a:{b:{c:0,d:4}}}
So this can be done using defaultdict:
D = defaultdict(defaultdict(Counter))
How do i use setdefault here?
EDIT :
Is it possible to combine setdefault and defaultdict ?
To build a multi-level dictionary with setdefault() you'd need to repeatedly access the keys like this:
>>> from collections import Counter
>>> d = {}
>>> d.setdefault("i", {}).setdefault("j", Counter())
Counter()
>>> d
{'i': {'j': Counter()}}
To generalize the usage for new keys you could use a function:
def get_counter(d, i, j):
return d.setdefault(i, {}).setdefault(j, Counter())
If I have two dictionaries that looks like this:
a = {"fruit":["orange", "lemon"], "vegetable":["carrot", "tomato"]}
b = {"fruit":["banana", "lime"]}
Is there a way I can update dictionary 'a' so that I don't overwrite the previous data, but simply append it so that my result would look like this?
a = {"fruit":["orange", "lemon", "banana", "lime"], "vegetable": ["carrot", "tomato"]}
I know there is something similar , but unfortunately it rewrites the values, which is not what I am looking to do:
a.update(b)
#returns a dictionary like the following {"fruit":["banana", "lime"], "vegetable":["carrot","tomato"]}, again, not what I want.
No way without a loop:
for k, v in b.items():
a[k].extend(v)
This assumes that a[k] actually exists . . . If you want to add it in the case where it is missing:
for k, v in b.items():
try:
a[k].extend(v)
except KeyError:
a[k] = v
You could use a defaultdict, but you have to iterate over the items.
from collections import defaultdict
a = defaultdict(list)
You could also define a helper method (but be careful not to call it with a normal dict, some type check may be appropriate):
def update(a,b):
for k, v in b.items():
a[k].extend(v)
The other option is to extend dict and override the update method to do it there.
You can also do a simple while loop:
accesos = {'carlos pinto': 23849284}
while True:
nueva_persona = input("Nombre?: ")
nueva_clave = input("Clave?: ")
accesos[nueva_persona] = nueva_clave
print(accesos)
If a and b are 2 dictionaries:
a = {'UK':'http://www.uk.com', 'COM':['http://www.uk.com','http://www.michaeljackson.com']}
bb = {'Australia': 'http://www.australia.com', 'COM':['http://www.Australia.com', 'http://www.rafaelnadal.com','http://www.rogerfederer.com']}
I want to merge them to get
{'Australia': ['http://www.australia.com'], 'COM': ['http://www.uk.com', 'http://www.michaeljackson.com', 'http://www.Australia.com', 'http://www.rafaelnadal.com', 'http://www.rogerfederer.com'], 'UK': ['http://www.uk.com']}
I want to union them i.e.
How to do it in Python without overwwriting and replacing any value?
Use a defaultdict:
from collections import defaultdict
d = defaultdict(list)
for dd in (a,bb):
for k,v in dd.items():
#Added this check to make extending work for cases where
#the value is a string.
v = (v,) if isinstance(v,basestring) else v #basestring is just str in py3k.
d[k].extend(v)
(but this is pretty much what I told you in my earlier answer)
This now works if your input dictionaries look like
{'Australia':['http://www.australia.com']}
or like:
{'Australia':'http://www.australia.com'}
However, I would advise against the latter form. In general, I think it's a good idea to keep all the keys/values of a dictionary looking the same (at least if you want to treat all the items the same as in this question). That means that if one value is a list, it's a good idea for all of them to be a list.
If you really insist on keeping things this way:
d = {}
for dd in (a,b):
for k,v in dd.items():
if(not isinstance(v,list)):
v = [v]
try:
d[k].extend(v)
except KeyError: #no key, no problem, just add it to the dict.
d[k] = v
So I've got a comprehension to the effect of:
dict((x.key, x.value) for x in y)
The problem, of course, is that if there's multiple x.keys with the same value, they get collapsed with the last x.value with that particular x.key as the only surviving member. I want to actually make the values of the resulting dict a list:
{
'key1': ['value1'],
'key2': ['value2', 'value3', 'value4'],
'key3': ['value5'],
# etc.
}
Is this logic possible with a comprehension?
You can add the elements one by one to a dictionary that contains empty lists by default:
import collections
result_dict = collections.defaultdict(list)
for x in y:
result_dict[x.key].append(x.value)
You can also do something very similar without having to use the collections module:
result_dict = {}
for x in y:
result_dict.setdefault(x.key, []).append(x.value)
but this is arguably slightly less legible.
An equivalent, more legible (no need to "parse" the less common setdefault) but more pedestrian, base Python approach is:
result_dict = {}
for x in y:
if x.key not in result_dict:
result_dict[x.key] = []
result_dict[x.key].append(x.value)
The first solution is clearly the preferred one, as it is at the same time concise, legible, and fast.
Nope. You cannot do this in a comprehension.
But you can use itertools.groupby.
I'm not saying it's the right thing to do, but, just out of sheer intellectual curiosity..
You can use itertools.groupby and lambda to do it in one dict comprehension, if that's what you really want to do: (where l is the list of tuples you want to make a dict out of:
dict((k, [v[1] for v in vs]) for (k, vs) in itertools.groupby(l, lambda x: x[0]))