How to create nested dictionaries PYTHON - python

This problem is similar to another where I learned How to Create Nested Dictionary in Python with 3 lists
How can I achieve an output such from 3 lists that takes the following output form
a = ['A', 'B', 'C', 'D']
b = [1,2,3,4]
c = ['N1', 'N2', 'N3', N4]
output = [{'A': {'N1':1}}, {'B':{'N2':2}}, {'C':{'N3':3}}, {'D':{'N4':4}}]
The output is different than on the posted link.
If I could describe this I would say it is a dictionary where 'N#' are the keywords for the values 1-4, and in turn those dictionaries have keyword 'A'-'D', which all seem to be dictionaries insided some top level keyword not shown under which they all are the elements of that master keyword.
I used this line of code which provided an output. These are different values than I'm using, but the point is this doesn't have as many curly brackets or the square brackets at the edges
d = {k: {x: y} for k, x, y in zip(a, b, c)}
# output: {'A':{'1' :'9'} , 'B':{'2':'8'}, 'C':{'3':'7'} , 'D':{'4':'6'}}
So I've explained what I think it means but I'm not sure how to proceed. I tried doing something like
d = {w:{k:{x:y}}for w,k,x,y in zip(sound, a, b, c)}
where 'sound' is a list of the same element value but that just prints out the 4th things on the list. Any help to clear this up would be much appreciated. Thanks.

Use a list comprehension. Iterate over the zipped lists and create the nested dictionaries:
a = ['A', 'B', 'C', 'D']
b = [1, 2, 3, 4]
c = ['N1', 'N2', 'N3', 'N4']
>>> [{k1: {k2: v}} for k1, v, k2 in zip(a, b, c)]
[{'A': {'N1': 1}}, {'B': {'N2': 2}}, {'C': {'N3': 3}}, {'D': {'N4': 4}}]

The dict comprehension is good by #mhawke, but let me add a more naive solution that may need to understand how to implement this type of nested Dictionary:
a = ['A', 'B', 'C', 'D']
b = [1, 2, 3, 4]
c = ['N1', 'N2', 'N3', 'N4']
output = [{'A': {'N1':1}}, {'B':{'N2':2}}, {'C':{'N3':3}}, {'D':{'N4':4}}]
def create_nested(a, b, c):
result = []
for i in range(len(a)):
parent = {}
child = {c[i]: b[i]}
parent.setdefault(a[i], child)
result.append(parent)
return result
result = create_nested(a, b, c)
print(result)
assert result == output
Using enumerate
def create_nested(a, b, c):
result = []
for idx, value in enumerate(a):
child = {c[idx]: b[idx]}
parent = {a[idx]: child}
result.append(parent)
return result
Shorter form
for idx, value in enumerate(a):
parent = {a[idx]: {c[idx]: b[idx]}}
result.append(parent)
return result
Enumerate

Related

Converting a dictionary with lists into a list of dictionaries

I have this dictionary containing lists as they're values for keys.
d = {'n': ['a', 'b', 'x'], 'a': [1, 2, 3], 'p': ['123', '321', '456']}
And I want to convert into a list of dictionary as such
[{'n': 'a', 'a': 1, 'p': '123'}, {'n': 'b', 'a': 2, 'p':
'321'}, {'n': 'x', 'a': 3, 'p': '456'}]
My current solution is,
the_kv = d.items()
f = {}
c = {}
e = {}
f_c_e = []
for i, j in the_kv:
f[i] = j[0]
c[i] = j[1]
e[i] = j[2]
f_c_e.append(f)
f_c_e.append(c)
f_c_e.append(e)
print(f_c_e)
But I was wondering if there is a more efficient way rather than creating separate dicts and then appending them to a list.
Using zip:
[dict(zip(d, vals)) for vals in zip(*d.values())]
Result:
[{'n': 'a', 'a': 1, 'p': '123'}, {'n': 'b', 'a': 2, 'p': '321'}, {'n': 'x', 'a': 3, 'p': '456'}]
Use:
res = [dict(v) for v in zip(*[[(key, value) for value in values] for key, values in d.items()])]
print(res)
Output
[{'n': 'a', 'a': 1, 'p': '123'}, {'n': 'b', 'a': 2, 'p': '321'}, {'n': 'x', 'a': 3, 'p': '456'}]
A simpler alternative approach is to do:
result = [{} for _ in range(len(d))]
for key, values in d.items():
for i, value in enumerate(values):
result[i][key] = value
print(result)
Output (alternative)
[{'n': 'a', 'a': 1, 'p': '123'}, {'n': 'b', 'a': 2, 'p': '321'}, {'n': 'x', 'a': 3, 'p': '456'}]
Let's work through the problem step by step.
The core difficulty is that we have a sequence of lists:
['a', 'b', 'x']
[1, 2, 3]
['123', '321', '456']
And we want to produce sequences consisting of element 0 of each list, element 1 of each list, etc. (Each of these sequences contains all the values for an output dict.) Which is to say, we want to transpose the lists, which is exactly what the built-in zip is for:
# Feed the generator to `list` to unpack and view them
list(zip(
['a', 'b', 'x'],
[1, 2, 3],
['123', '321', '456']
))
Now we can sketch out a complete process:
Get the keys and values of the input (in the same order).
Transpose the values.
For each sequence in the transposed values, match that sequence up with the keys to make a new dict.
The first two parts are easy:
keys, value_lists = d.keys(), d.values()
# Since the .values() are a sequence, while `zip` accepts multiple
# arguments, we need to use the `*` operator to unpack them as
# separate arguments.
grouped_values = zip(*value_lists)
To finish up, let's first figure out how to create a single result dict from a single one of the new_values. It's easy to make a dict from a bunch of key-value pairs - we can feed that directly to dict. However, we have instead a pair of sequences - the original .keys(), and a result from the zip. Clearly, the solution is to zip again - we can make a trivial helper function to make sure everything is as clear as possible:
def new_dict(keys, values):
return dict(zip(keys, values))
Then all we need to do is repeatedly apply that function:
new_dicts = [new_dict(keys, values) for values in grouped_values]
Putting everything inline with short names for the sake of showing off gives:
new_dicts = [dict(zip(d.keys(), v)) for v in zip(*d.values())]
which is almost exactly Jab's answer (note that iterating over a dictionary gives the keys, so you can pass d directly rather than d.keys() to zip since zip will only be iterating over it).
Good Question.
Conversion from one data type to other is essential in either development or competitive programming.
It can be done in many methods, I'm explaining the two methods which I know, below:
Method #1 : Using list comprehension
We can use list comprehension as the one-liner alternative to perform various naive tasks providing readability with a more concise code. We can iterate through each of dictionary element and corresponding keep constructing the list of dictionary.
# Python3 code to demonstrate
# to convert dictionary of list to
# list of dictionaries
# using list comprehension
# initializing dictionary
test_dict = { "Rash" : [1, 3], "Manjeet" : [1, 4], "Akash" : [3, 4] }
# printing original dictionary
print ("The original dictionary is : " + str(test_dict))
# using list comprehension
# to convert dictionary of list to
# list of dictionaries
res = [{key : value[i] for key, value in test_dict.items()}
for i in range(2)]
# printing result
print ("The converted list of dictionaries " + str(res))
Method #2 : Using zip()
This approach used zip function two times, first time when we need to zip the particular index value of all lists as one and second to get all values of particular index and zip it with the corresponding keys.
# Python3 code to demonstrate
# to convert dictionary of list to
# list of dictionaries
# using zip()
# initializing dictionary
test_dict = { "Rash" : [1, 3], "Manjeet" : [1, 4], "Akash" : [3, 4] }
# printing original dictionary
print ("The original dictionary is : " + str(test_dict))
# using zip()
# to convert dictionary of list to
# list of dictionaries
res = [dict(zip(test_dict, i)) for i in zip(*test_dict.values())]
# printing result
print ("The converted list of dictionaries " + str(res))
Output
The original dictionary is : {‘Rash’: [1, 3], ‘Manjeet’: [1, 4], ‘Akash’: [3, 4]}
The converted list of dictionaries [{‘Rash’: 1, ‘Manjeet’: 1, ‘Akash’: 3}, {‘Rash’: 3, ‘Manjeet’: 4, ‘Akash’: 4}]

zip two unequal lists into a dictionary. give none if key has no value. ignore value without a key

There are two lists of different lengths. The first contains keys, and the second contains values.
Write a function that creates a dictionary from these keys and values.
If the key did not have enough values, the dictionary should have the value None.
Values that did not have enough keys should be ignored.
a = ['a', 'b', 'c']
b = [1, 2, 3, 4, 5]
dict(zip(a, b))
I need this:
{'a':'1', 'b':'2', 'c':'4'}
for
a = ['a', 'b', 'c']
b = [1, 2]
dict(zip(a, b))
I need this:
{'a':'1', 'b':'2', 'c':'None'}
Build an iterator from b and iterate over a getting the next element from b, defaulting to None, e.g.
it = iter(b)
{k: next(it, None) for k in a}
itertools.zip_longest is what you need:
from itertools import zip_longest
dict(zip_longest(a,b) if len(a)>len(b) else zip(a,b))
if part is to avoid None as keys.
Dict comprehension way:
{key: b[idx] if idx < len(b) else None for idx, key in enumerate(a)}
You can add elements to a list using + operator and create a series of new elements using the * operator. So I would solve your problem like this:
dict( zip( a, b + [None] * (len(a)-len(b)) ) )
Which extends the list b in case it is shorter than the list a with None elements and zips both together.
The output:
>>> a = ['a','b','c']
>>> b = [1,2]
>>> dict( zip( a, b + [None] * (len(a)-len(b)) ) )
{'a': 1, 'b': 2, 'c': None}
>>> b = [1,2,3,4,5]
>>> dict( zip( a, b + [None] * (len(a)-len(b)) ) )
{'a': 1, 'b': 2, 'c': 3}
Try this:
a = ['a','b','c']
b = [1,2,3,4,5]
while len(b) < len(a):
b.append(None)
c = dict(zip(a,b))
print(c)

How do I swap two random values from a dictionary?

I need to swap two random values from a dicitonary
def alphabetcreator():
letters = random.sample(range(97,123), 26)
newalpha = []
engalpha =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
alphasmerged = {}
for i in letters:
newalpha.append(chr(i))
alphasmerged = dict(zip(engalpha, newalpha))
return(alphabetsmerged)
This code gives me my two different alphabets, putting them into a dictionary so I can translate between one and the other. I now need to randomly swap two of the values whilst keeping all the rest the same. How can I do this?
You can first use random.sample to randomly pick two different values from a collection.
From the doc:
Return a k length list of unique elements chosen from the population sequence or set. Used for random sampling without replacement.
Use this function on the keys of your dictionary to have two distinct keys.
In Python 3, you can directly use it on a dict_keys object.
In Python 2, you can either convert d.keys() into a list, or directly pass the dictionary to the sample.
>>> import random
>>> d = {'a': 1, 'b': 2}
>>> k1, k2 = random.sample(d.keys(), 2) # Python 3
>>> k1, k2 = random.sample(d, 2) # Python 2
>>> k1, k2
['a', 'b']
Then, you can in-place-ly swap two values of a collection.
>>> d[k1], d[k2] = d[k2], d[k1]
>>> d
{'b': 1, 'a': 2}
d = {12: 34, 67: 89}
k, v = random.choice(list(d.items()))
d[v] = k
d.pop(k)
which when running, gave the random output of d as:
{12: 34, 89: 67}
You can try this:
import random
engalpha =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
new_dict = {a:b for a, b in zip(engalpha, map(chr, random.sample(range(97,123), 26)))}
key_val = random.choice(list(new_dict.keys()))
final_dict = {b if a == key_val else a:a if a == key_val else b for a, b in new_dict.items()}
Regarding your recent comment:
import random
s = {'a': 'h', 'b': 'd', 'c': 'y'}
random_dict = [(a, b) for a, b in random.sample(list(s.items()), 2)]
new_dict = {a:b for a, b in zip([i[0] for i in sorted(random_dict, key=lambda x:x[0])], [i[-1] for i in sorted(random_dict, key=lambda x:x[-1])][::-1])}
final_dict = {a:new_dict.get(a, b) for a, b in s.items()}
Output (randomly generated):
{'a': 'y', 'c': 'h', 'b': 'd'}

create a dictionary with incrementing values

I have a list and I want to generate a dictionary d taking out duplicates and excluding a single item, such that the first key has value 0, the second has value 1, and so on.
I have written the following code:
d = {}
i = 0
for l in a_list:
if (l not in d) and (l != '<'):
d[l] = i
i += 1
If a_list = ['a', 'b', '<', 'c', 'b', 'd'], after running the code d contains {'a': 0, 'b': 1, 'c': 2, 'd':3}. Order is not important.
Is there a more elegant way to obtain the same result?
Use dict.fromkeys to get your unique occurrences (minus values you don't want), then .update it to apply the sequence, eg:
a_list = ['a', 'b', '<', 'c', 'b', 'd']
d = dict.fromkeys(el for el in a_list if el != '<')
d.update((k, i) for i, k in enumerate(d))
Gives you:
{'a': 0, 'b': 1, 'd': 2, 'c': 3}
If order is important, then use collections.OrderedDict.fromkeys to retain the ordering of the original values, or sort the unique values if they should be alphabetical instead.
{b: a for a, b in enumerate(set(a_list) - {'<'})}
set(a_list) creates a set from a_list.
That effectively strips duplicate numbers in a_list, as a set can only contain unique values.
What is needed here is an ordereddict and to manually filter the list:
from collections import OrderedDict
d = OrderedDict()
new_list = []
a_list = [1,3,2,3,2,1,3,2,3,1]
for i in a_list:
if i not in new_list:
new_list.append(i)
for i, a in enumerate(new_list):
if a != "<":
d[i] = a
Output:
OrderedDict([(0, 1), (1, 3), (2, 2)])
If original order is not important:
final_d = {i:a for i, a in enumerate(set(a_list)) if a != "<"}
I personally find recursion quite elegant, tail-recursion especially so:
def f( d, a_list ):
if a_list:
if a_list[0] not in d and a_list[0] != '<':
d[a_list[0]] = len(d)
return f( d, a_list[1:] )
else:
return d
So that
f( {}, "acbcbabcbabcb" )
will yield
{'a': 0, 'c': 1, 'b': 2}
just like the original code does on the same input (modulo order of the keys).
If truly:
Order is not important.
{k: i for i, k in enumerate(filter(lambda x: x not in "<", set(a_list)))}
# {'a': 3, 'b': 1, 'c': 0, 'd': 2}
EDIT: #qnnnnez's answer takes advantage of set operations, giving an elegant version of the latter code.
Otherwise you can implement the unique_everseen itertools recipe to preserve order. For convenience, you can import it from a library that implements this recipe for you, i.e. more_itertools.
from more_itertools import unique_everseen
{k: i for i, k in enumerate(filter(lambda x: x not in "<", unique_everseen(a_list)))}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3}

Iterate over dictionary of class containing a list

I have something like this:
class C:
def get_data(): returns a list
d = {several instances of C}
Now, how do I iterate over all elements of all lists? I failed miserably:
[e for e in [v.get_data() for _, v in d.items()]]
The inner loop will produce a list of lists which can not be digested by the outer loop.
You want a flatten:
[e for _, v in d.items() for e in v.get_data()]
Note the order of the loops.
import itertools
class C:
def get_data(self): return ['a','b']
d = {'a' : C(), 'b' : C()}
print [e for e in itertools.chain.from_iterable(v.get_data() for v in d.values())]
Will print
['a', 'b', 'a', 'b']
Just modify your existing expression -
[item for sublist in [v.get_data() for _, v in d.items()] for item in sublist]
#piokuc's answer is the better option, but in Python 3, yield from is a possibility:
class C:
def get_data(self):
return ['a','b']
def extract(dict_):
"""Yield elements from sublists."""
for _, v in dict_.items():
yield from v.get_data()
d = {'a' : C(), 'b' : C()}
[e for e in extract(d)]
# ['a', 'b', 'a', 'b']
Not sure what you mean by "iterate over all elements", but based on the code in your question, the following will create a list of all the elements in both Python 2 & 3 and doesn't require importing anything else:
class C:
def get_data(self): return ['a', 'b']
d = {'a' : C(), 'b' : C()}
print([e for c in d.values() for e in c.get_data()]) # -> ['a', 'b', 'a', 'b']
In Python 2 you might want to use d.itervalues() instead of d.values() because it creates an iterator of the values rather than creating a temporary list of them (which is what Python 3 does automatically when the values() method called).

Categories

Resources