I have a dictionary of dictionaries as shown below:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
and I would want it be in required format:
result = {1:["hello", "!"], 2: ["How are you", "?"]}
However, I get this in the following format using the code below:
new_d = {}
for sub in d.values():
for key, value in sub.items():
new_d.setdefault(key, []).append(value)
The result is not of required structure and it causes a list of lists.
{1: [['hello'], ['!']], 2: [['How are you'], ['?']]}
Any help here would be highly appreciated. Thanks.
use extend instead of append:
new_d.setdefault(key, []).extend(value)
The extend() method adds all the elements of an iterable (list, tuple, string etc.) to the end of the list.
If you want to solve this problem with using append() function try this code:
new_d = {}
for sub in d.values():
for key, value in sub.items():
# Control key exist...
if(key in new_d.keys()):
new_d[key].append(value[0])
else:
new_d[key] = value
You can either use .extend(value) instead of .append(value)
or you can add a basic for loop to flatten the list of all dictionary values as shown below.
new_d = {}
for sub in d.values():
for key, value in sub.items():
new_d.setdefault(key, []).extend(value)
for i in range (0,len(d)):
new_d[i+1] = [item for sublist in new_d.get(i+1) for item in sublist]
print(new_d)
The accepted answer by #Gabip correctly identifies that your only mistake was using append instead of extend.
That mistake being corrected, I'd also like to suggest a slightly different approach using dict comprehensions:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
new_d = {key: d[0].get(key, []) + d[1].get(key, []) for key in d[0]}
# {1: ['hello', '!'], 2: ['How are you', '?']}
Or a more robust version that takes keys from both d[0] and d[1], in case some keys are in d[1] but not in d[0]:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"], 3: ['>>>']}}
new_d = {key: d[0].get(key, []) + d[1].get(key, []) for key in set(d[0].keys()) | set(d[1].keys())}
# {1: ['hello', '!'], 2: ['How are you', '?'], 3: ['>>>']}
Finally, this wasn't explicitly part of your question, but I suggest using str.join to join the strings:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
new_d = {key: ''.join(d[0].get(key, []) + d[1].get(key, [])) for key in d[0]}
# {1: 'hello!', 2: 'How are you?'}
Related
Having a dictionary as below:
a_dict = {1: 'blue', 2: 'apple', 3: 'dog'}
need to reduce the key value by one and drop the blue value.
output:
a_dict = {1: 'apple', 2: 'dog'}
What you want to do is a bit strange (what is the real underlying goal?)
One option, assuming you want to keep the same order, and shift the values after blue to one key before:
l = list(a_dict.values())
l.remove('blue')
d = dict(zip(a_dict, l))
Output: {1: 'apple', 2: 'dog'}
NB. In case of multiple 'blue', this would only remove the first one. To remove all:
d = dict(zip(a_dict, [v for v in a_dict.values() if v != 'blue']))
dropping the first value
If you already know that the value to drop if the first one:
out = dict(zip(a_dict, list(a_dict.values())[1:]))
Or, more efficient:
i = iter(a_dict.values())
next(i) # consume first value
out = dict(zip(a_dict, i))
Another solution, with := operator:
a_dict = {1: "blue", 2: "apple", 3: "dog"}
i = 0
a_dict = {i: v for v in a_dict.values() if v != "blue" and (i := i + 1)}
print(a_dict)
Prints:
{1: 'apple', 2: 'dog'}
I'd like to group by the values of the following dictionary:
my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
"Q3": {1: "animal", 2: "vehicle"}, Q4: {1: "animal", 2: "vehicle"}}
The result should look like this:
result = {("Q1", "Q2"): {0: "no", 1: "yes"},
("Q3", "Q4"): {1: "animal", 2: "vehicle"}}
I've tried the solutions listed here:
Grouping Python dictionary keys as a list and create a new dictionary with this list as a value
Using collections.defaultdict does not work because the result would imply that the dictionaries which I use as a key for grouping end up as keys of the result dictionary like that:
result = {{0: "no", 1: "yes"}: ["Q1", "Q2"] ,
{1: "animal", 2: "vehicle"}: ["Q3", "Q4"]}
Of course this does not work because keys of dictionaries have to be immutible. So I would require something like a frozendict which is not available in the standard library of python.
Using itertools.groupby also does not work because it requires the data to be sorted. But operator.itemgetter cannot sort dictionaries. It says:
TypeError: '<' not supported between instances of 'dict' and 'dict'
Therefore, I'd like to know a Pythonic way of solving this problem! Thank you for your help :)
Instead of using frozendict, you can use frozenset's of the dictionaries' items:
intermediate_dict = defaultdict(list)
for k, v in my_dict.items():
intermediate_dict[frozenset(v.items())].append(k)
result = {tuple(v): dict(k) for k, v in intermediate_dict.items()}
Output:
{('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}
The above does not assume or require sorted input, making it O(n) for all cases, while sorting is O(n log n).
Assuming a sorted dictionary by value, you can use itertools.groupby:
{tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)}
Code:
from itertools import groupby
my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
"Q3": {1: "animal", 2: "vehicle"}, "Q4": {1: "animal", 2: "vehicle"}}
print({tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)})
# {('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}
So I would require something like a frozendict which is not available in the standard library of python.
Could you elaborate on this? While frozendict is not in the language standard, there's an extension available that you could install: https://pypi.org/project/frozendict/
Alternatively, you can turn the dictionaries into a tuple of (key-sorted) (key, value) items to get an immutable, canonical and reversible representation that can be used as a dictionary key.
(Note that if the dictionaries can have further mutable values inside them, you might need to do this recursively.)
Edit: Or use a frozenset() for the items, as the other answer points out. Note that this also requires recursively ensuring the values of the inner dictionary are immutable.
Here is another way using both frozenset and groupby
from operator import itemgetter
from itertools import groupby
first = itemgetter(0)
second = itemgetter(1)
my_hashes = sorted([(k, hash(frozenset(v))) for k, v in my_dict.items()], key=second)
d = dict()
for k, v in groupby(my_hashes, key=second):
items = list(v)
d[tuple(map(first, items))] = my_dict.get(first(first(items)))
print(d)
{('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}, ('Q1', 'Q2'): {0: 'no', 1: 'yes'}}
I have two python lists:
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
The "keys" are cluster ID list for the corresponding words in "values" list. I wish to print key-value pairs using
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
dictionary = dict(zip(keys, values))
for key, value in dictionary.items() :
print (key, value)
But it only prints
1 apple
2 paper
3 tennis
What I actually want is to get all values for all keys like this
1 [apple]
2 [book,pen,paper]
3 [soccer,tennis]
I know that my current code should logically print the first output as keys are unique. But how can I change it so that it will print all values for all keys? Thank you in advance!
from collections import defaultdict
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
d = defaultdict(list)
for k, v in zip(keys, values):
d[k].append(v)
Looks like what you want is a mapping from one key to multiple values, one way to accomplish it would be:
from collections import defaultdict
d = defaultdict(list)
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
for tuple in zip(keys, values):
d[tuple[0]].append(tuple[1])
print(d) # defaultdict(<class 'list'>, {1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']})
You can use itertools:
import itertools
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
final_data = {a:[i[0] for i in b] for a, b in [(a, list(b)) for a, b in itertools.groupby(sorted(zip(values, keys), key=lambda x:x[-1]), key=lambda x:x[-1])]}
Output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
pure python also works
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
d = dict(zip(keys, [[] for _ in keys])) # dict w keys, empty lists as values
for k, v in zip(keys, values):
d[k].append(v)
d
Out[128]: {1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
Two method :
If you want you can use default dict as many already have been suggested :
Data is :
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
Method: 1
import collections
d=collections.defaultdict(list)
for i in zip(keys,values):
d[i[0]].append(i[1])
print(d)
output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
Or if you want to develop your own logic without importing any external module then you can try:
result={}
for i in zip(keys,values):
if i[0] not in result:
result[i[0]]=[i[1]]
else:
result[i[0]].append(i[1])
print(result)
output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
I'm sure this can be done, but I have thus far been unsuccessful:
I have a list of strings. I want to create a dictionary with the length of said strings (which can be expressed as a range) as the key and the string itself as the value.
example:
Here's something like the list I have: ['foo','bar','help','this','guy']
I'd like to end up with a dictionary like this:
{3:['foo','bar','guy], 4:['this','help']}
Using defaultdict so you don't have to check whether or not to create the list for a new key:
from collections import defaultdict
x = ['foo','bar','help','this','guy']
len_dict = defaultdict(list)
for word in x:
len_dict[len(word)].append(word)
len_dict
#
# Out[5]: defaultdict(list, {3: ['foo', 'bar', 'guy'], 4: ['help', 'this']})
You can use a dictionary as a container with setdefault:
lst = ['foo','bar','help','this','guy']
result = {}
for w in lst:
result.setdefault(len(w), []).append(w)
result
# {3: ['foo', 'bar', 'guy'], 4: ['help', 'this']}
You can do it like that:
d={}
lst=['foo','bar','help','this','guy']
for i in lst:
if len(i) in d:
d[len(i)].append(i)
else:
d[len(i)]=[i]
This solution is pythonic, elegant and fast: (by the Famous Raymond Hettinger in one of his many conferences).
dict.setdefault is the dictionary method that initialises a key-value if the key is not found in dict as well as performing dict.get for provided key.
l = ['foo','bar','help','this','guy']
d = {}
for e in l:
key = len(e)
d.setdefault(key, []).append(name)
print(d)
Output:
{3: ['foo', 'bar', 'guy'], 4: ['help', 'this']}
This solution is the modern way of the solution above:
defaultdict from collection is a subclass of dict that automatically initialises value to any given key that is not in the defaultdict.
from collections import defaultdict
l = ['foo','bar','help','this','guy']
d = defaultdict(list)
for e in l:
key = len(e)
d[key].append(e)
print(d)
Output:
defaultdict(<class 'list'>, {3: ['foo', 'bar', 'guy'], 4: ['help', 'this']})
Similar to what have been said, but using the get method of dict class:
the_list=['foo','bar','help','this','guy']
d = {}
for word in the_list:
key = len(word)
d[key] = d.get(key, []) + [word]
print(d)
# {3: ['foo', 'bar', 'guy'], 4: ['help', 'this']}
Another approach:
from collections import defaultdict
given_list=['foo','bar','help','this','guy']
len_words=[len(i) for i in given_list]
d=defaultdict(list)
for i,j in list(zip(len_words,given_list)):
d[i].append(j)
I have two lists that are of the same length:
alist = ['XX', 'HH', 'GG', 'XX', 'II', 'PP', 'LL', 'TT', 'KK', 'XX']
blist = [2, 3, 5, 5, 9, 8, 9, 4, 7, 2]
I want to know what values in alist have the same number in blist. I want the outcome to look like this:
2 = XX; 3 = HH; 5 = GG, XX; 9 = II, LL; 8 = PP; 4 = TT; 7 = KK
I solved it like this:
from collections import defaultdict
adict = {}
a = zip(blist, alist)
for key, value in a:
adict.setdefault(k, []).append(v)
which gives this result:
adict:
{2: ['XX', 'XX'], 3: ['HH'], 4: ['TT'], 5: ['GG', 'XX'], 7: ['KK'], 8: ['PP'], 9: ['II', 'LL']}
but I dont want the same value twice, for example 2: ['XX', 'XX'] - I would like to have instead 2: ['XX'].
I tried this using 'set' before the list of values:
a = zip(blist, alist)
for key, value in a:
a.setdefault(k, set[]).append(v)
but it complained...
any ideas?
I'd personally use Martijn's defaultdict approach, but I thought I'd address the issues with your current attempt.
The problem with your code:
a = zip(blist, alist)
for key, value in a:
a.setdefault(k, set[]).append(v)
Is That:
after the zip, a is now a list, so it won't support .setdefault
k is not used in the for loop - it should be key
v is not used in the for loop = it should be value
set[] is invalid synatx - to create a new set - use set()
A set does not have an .append - instead you want to use add
Corrected code:
d = {}
a = zip(blist, alist)
for key, value in a:
d.setdefault(key, set()).add(value)
Adapt the following as desired to print:
for k, v in d.iteritems():
if k > 5:
print '{0}: {1}'.format(k, ','.join(v))
Use the defaultdict type you imported but otherwise ignore:
from collections import defaultdict
a = defaultdict(set)
for k, v in zip(blist, alist):
a[k].add(v)
results in:
>>> a
defaultdict(<type 'set'>, {2: set(['XX']), 3: set(['HH']), 4: set(['TT']), 5: set(['GG', 'XX']), 7: set(['KK']), 8: set(['PP']), 9: set(['II', 'LL'])})
defaultdict is a subclass of dict and behaves in the same way otherwise.
Use your code and then add:
a = {x: set(a[x]) for x in a }