Converting a dictionary with lists into a list of dictionaries - python

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}]

Related

filter keys out from list of dicts

Say I have a list of dict:
ld = [{'a':1,'b':2,'c':9},{'a':1,'b':2,'c':10}]
And a list to filter the keys out:
l = ['a','c']
Want to remove key a and c from ld:
Try:
result = [d for d in ld for k in d if k in l]
Desired Result:
[{'b':2},{'b':2}]
Your outer container needs to be a list : use a (1 dimension) list comprehension
Your inner container needs to be a dict : ues a dict comprehension
For you now you're using a 2d list comprehension
The filtering part should be at the dict level
ld = [{'a': 1, 'b': 2, 'c': 9}, {'a': 1, 'b': 2, 'c': 10}]
l = ['a', 'c']
result = [{k: v for k, v in subdict.items() if k not in l}
for subdict in ld]
print(result)
Your code is overly compressed and that makes it hard to understand and easy for bugs to hide.
Here you want to "filter" a list of dicts and remove entries from each dicht that is not in the filter list. So my first suggestion is to rename your variables:
data = [{'a': 1, 'b': 2, 'c': 9}, {'a': 1, 'b': 2, 'c': 10}]
excludes = ['a', 'c']
Now you need to unpack a list, and then the inner dict. Currently you are trying to use the list iteration on both. You want items() to iterate over (key, value) pairs.
Further, you are filtering on the outer list level, while the keys to filter live in the inner dict.
Here is my solution
result = []
for entry in data:
# entry is now one of the dicts
result.append({key:value for key, value in entry.items() if key not in excludes})
You can compress this again in a single line, if you really want to. But in my opinion (while not playing code golf) readability beats compressedness.
I think the simple writing method is clearer when there are many cycles
ld = [{'a':1,'b':2,'c':9},{'a':1,'b':2,'c':10}]
l = ['a','c']
for d in ld:
for r in l:
if r in d:
del d[r]
Alternative filtering whether keys in ld are not in l:
result = [{k:d[k]} for d in ld for k in d if k not in l]

Convert two dictionaries to a dictionary of dictionaries containing all combinations

Problem
I have two dictionaries: a and b
a={'e':[1,2]}
b={'d':[11,22]}
How can I convert them to a dictionary of dictioneries that contains all possible combinations of the lists [1,2] and [11,22]. The expected result should be:
dic={1:{'e':1,'d':11},
2:{'e':2,'d':22},
3:{'e':1,'d':11},
4:{'e':2,'d':22}}
My attemt:
I can easily get the combinations of two lists or any set of lists using itertools like so:
l=list(itertools.product([1,2],[11,22]))
But I don't know how to proceed from here. Any suggestions?
You almost have it. Just have to create dictionaries by mapping keys to the tuples in l.
keys = list(a.keys()) + list(b.keys())
dic = {k: dict(zip(keys, tpl)) for k, tpl in enumerate(l), 1)}
Here's a bit more generalized approach:
(i) First combine the dictionaries:
combined = {**a, **b}
(ii) Find the Cartesian product the list items in combined.values().
(iii) Iterate over outcome from (ii) and create dictionaries with each tuple and the keys in combined.keys():
dic = {k: dict(zip(combined.keys(), tpl)) for k, tpl in enumerate(itertools.product(*combined.values()), 1)}
Output:
{1: {'e': 1, 'd': 11}, 2: {'e': 1, 'd': 22}, 3: {'e': 2, 'd': 11}, 4: {'e': 2, 'd': 22}}
Given l as you calculated it you can use dict comprehension and achieve the required output -
{idx+1 : {'e': item[0], 'd': item[1]} for idx, item in enumerate(l)}
(BTW there is no need to convert the itertools.product result to list)

Create dictionary with alphabet characters mapping to numbers

I want to write a code in Python, which assigns a number to every alphabetical character, like so: a=0, b=1, c=2, ..., y=24, z=25. I personally don't prefer setting up conditions for every single alphabet, and don't want my code look over engineered. I'd like to know the ways I can do this the shortest (meaning the shortest lines of code), fastest and easiest.
(What's on my mind is to create a dictionary for this purpose, but I wonder if there's a neater and better way).
Any suggestions and tips are in advance appreciated.
You definitely want a dictionary for this, not to declare each as a variable. A simple way is to use a dictionary comprehension with string.ascii_lowercase as:
from string import ascii_lowercase
{v:k for k,v in enumerate(ascii_lowercase)}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5...
Here's my two cents, for loop will do the work:
d = {} #empty dictionary
alpha = 'abcdefghijklmnopqrstuvwxyz'
for i in range(26):
d[alpha[i]] = i #assigns the key value as alphabets and corresponding index value from alpha string as the value for the key
print(d) #instant verification that the dictionary has been created properly
One-liner with map and enumerate:
# given
foo = 'abcxyz'
dict(enumerate(foo))
# returns: {0: 'a', 1: 'b', 2: 'c', 3: 'x', 4: 'y', 5: 'z'}
If you needed it with the characters as the dictionary keys, what comes into my mind is either a dict comprehension...
{letter:num for (num,letter) in enumerate(foo) }
# returns {'a': 0, 'b': 1, 'c': 2, 'z': 3, 'y': 4, 'x': 5}
... or a lambda...
dict( map(lambda x: (x[1],x[0]), enumerate(foo)) )
# returns {'a': 0, 'b': 1, 'c': 2, 'z': 3, 'y': 4, 'x': 5}
I feel dict comprehension is much more readable than map+lambda+enumerate.
There are already numbers associated with characters. You can use these code points with ord().
A short (in terms of lines) solution would be:
num_of = lambda s: ord(s) - 97
A normal function would be easier to read:
def num_of(s):
return ord(s) - 97
Usage:
num_of("a") # 0
num_of("z") # 25
If it must be a dictionary you can create it without imports like that:
{chr(n):n-97 for n in range(ord("a"), ord("z")+1)}

Could anyone explain this syntax? [duplicate]

Can I use list comprehension syntax to create a dictionary?
For example, by iterating over pairs of keys and values:
d = {... for k, v in zip(keys, values)}
Use a dict comprehension (Python 2.7 and later):
{key: value for (key, value) in iterable}
Alternatively for simpler cases or earlier version of Python, use the dict constructor, e.g.:
pairs = [('a', 1), ('b', 2)]
dict(pairs) #=> {'a': 1, 'b': 2}
dict([(k, v+1) for k, v in pairs]) #=> {'a': 2, 'b': 3}
Given separate arrays of keys and values, use the dict constructor with zip:
keys = ['a', 'b']
values = [1, 2]
dict(zip(keys, values)) #=> {'a': 1, 'b': 2}
2) "zip'ped" from two separate iterables of keys/vals
dict(zip(list_of_keys, list_of_values))
In Python 3 and Python 2.7+, dictionary comprehensions look like the below:
d = {k:v for k, v in iterable}
For Python 2.6 or earlier, see fortran's answer.
In fact, you don't even need to iterate over the iterable if it already comprehends some kind of mapping, the dict constructor doing it graciously for you:
>>> ts = [(1, 2), (3, 4), (5, 6)]
>>> dict(ts)
{1: 2, 3: 4, 5: 6}
>>> gen = ((i, i+1) for i in range(1, 6, 2))
>>> gen
<generator object <genexpr> at 0xb7201c5c>
>>> dict(gen)
{1: 2, 3: 4, 5: 6}
Create a dictionary with list comprehension in Python
I like the Python list comprehension syntax.
Can it be used to create dictionaries too? For example, by iterating
over pairs of keys and values:
mydict = {(k,v) for (k,v) in blah blah blah}
You're looking for the phrase "dict comprehension" - it's actually:
mydict = {k: v for k, v in iterable}
Assuming blah blah blah is an iterable of two-tuples - you're so close. Let's create some "blahs" like that:
blahs = [('blah0', 'blah'), ('blah1', 'blah'), ('blah2', 'blah'), ('blah3', 'blah')]
Dict comprehension syntax:
Now the syntax here is the mapping part. What makes this a dict comprehension instead of a set comprehension (which is what your pseudo-code approximates) is the colon, : like below:
mydict = {k: v for k, v in blahs}
And we see that it worked, and should retain insertion order as-of Python 3.7:
>>> mydict
{'blah0': 'blah', 'blah1': 'blah', 'blah2': 'blah', 'blah3': 'blah'}
In Python 2 and up to 3.6, order was not guaranteed:
>>> mydict
{'blah0': 'blah', 'blah1': 'blah', 'blah3': 'blah', 'blah2': 'blah'}
Adding a Filter:
All comprehensions feature a mapping component and a filtering component that you can provide with arbitrary expressions.
So you can add a filter part to the end:
>>> mydict = {k: v for k, v in blahs if not int(k[-1]) % 2}
>>> mydict
{'blah0': 'blah', 'blah2': 'blah'}
Here we are just testing for if the last character is divisible by 2 to filter out data before mapping the keys and values.
In Python 2.7, it goes like:
>>> list1, list2 = ['a', 'b', 'c'], [1,2,3]
>>> dict( zip( list1, list2))
{'a': 1, 'c': 3, 'b': 2}
Zip them!
Python version >= 2.7, do the below:
d = {i: True for i in [1,2,3]}
Python version < 2.7(RIP, 3 July 2010 - 31 December 2019), do the below:
d = dict((i,True) for i in [1,2,3])
To add onto #fortran's answer, if you want to iterate over a list of keys key_list as well as a list of values value_list:
d = dict((key, value) for (key, value) in zip(key_list, value_list))
or
d = {(key, value) for (key, value) in zip(key_list, value_list)}
Just to throw in another example. Imagine you have the following list:
nums = [4,2,2,1,3]
and you want to turn it into a dict where the key is the index and value is the element in the list. You can do so with the following line of code:
{index:nums[index] for index in range(0,len(nums))}
Here is another example of dictionary creation using dict comprehension:
What i am tring to do here is to create a alphabet dictionary where each pair; is the english letter and its corresponding position in english alphabet
>>> import string
>>> dict1 = {value: (int(key) + 1) for key, value in
enumerate(list(string.ascii_lowercase))}
>>> dict1
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'g': 7, 'f': 6, 'i': 9, 'h': 8,
'k': 11, 'j': 10, 'm': 13, 'l': 12, 'o': 15, 'n': 14, 'q': 17, 'p': 16, 's':
19, 'r': 18, 'u': 21, 't': 20, 'w': 23, 'v': 22, 'y': 25, 'x': 24, 'z': 26}
>>>
Notice the use of enumerate here to get a list of alphabets and their indexes in the list and swapping the alphabets and indices to generate the key value pair for dictionary
Hope it gives a good idea of dictionary comp to you and encourages you to use it more often to make your code compact
This code will create dictionary using list comprehension for multiple lists with different values that can be used for pd.DataFrame()
#Multiple lists
model=['A', 'B', 'C', 'D']
launched=[1983,1984,1984,1984]
discontinued=[1986, 1985, 1984, 1986]
#Dictionary with list comprehension
keys=['model','launched','discontinued']
vals=[model, launched,discontinued]
data = {key:vals[n] for n, key in enumerate(keys)}
#Convert dict to dataframe
df=pd.DataFrame(data)
display(df)
enumerate will pass n to vals to match each key with its list
Try this,
def get_dic_from_two_lists(keys, values):
return { keys[i] : values[i] for i in range(len(keys)) }
Assume we have two lists country and capital
country = ['India', 'Pakistan', 'China']
capital = ['New Delhi', 'Islamabad', 'Beijing']
Then create dictionary from the two lists:
print get_dic_from_two_lists(country, capital)
The output is like this,
{'Pakistan': 'Islamabad', 'China': 'Beijing', 'India': 'New Delhi'}
Adding to #Ekhtiar answer, if you want to make look up dict from list, you can use this:
names = ['a', 'b', 'd', 'f', 'c']
names_to_id = {v:k for k, v in enumerate(names)}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'f': 4}
Or in rare case that you want to filter duplicate, use set first (best in list of number):
names = ['a', 'b', 'd', 'f', 'd', 'c']
sorted_list = list(set(names))
sorted_list.sort()
names_to_id = {v:k for k, v in enumerate(sorted_list)}
# {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'f': 4}
names = [1,2,5,5,6,2,1]
names_to_id = {v:k for k, v in enumerate(set(names))}
# {1: 0, 2: 1, 5: 2, 6: 3}
>>> {k: v**3 for (k, v) in zip(string.ascii_lowercase, range(26))}
Python supports dict comprehensions, which allow you to express the creation of dictionaries at runtime using a similarly concise syntax.
A dictionary comprehension takes the form {key: value for (key, value) in iterable}. This syntax was introduced in Python 3 and backported as far as Python 2.7, so you should be able to use it regardless of which version of Python you have installed.
A canonical example is taking two lists and creating a dictionary where the item at each position in the first list becomes a key and the item at the corresponding position in the second list becomes the value.
The zip function used inside this comprehension returns an iterator of tuples, where each element in the tuple is taken from the same position in each of the input iterables. In the example above, the returned iterator contains the tuples (“a”, 1), (“b”, 2), etc.
Output:
{'i': 512, 'e': 64, 'o': 2744, 'h': 343, 'l': 1331, 's': 5832, 'b': 1, 'w': 10648, 'c': 8, 'x': 12167, 'y': 13824, 't': 6859, 'p': 3375, 'd': 27, 'j': 729, 'a': 0, 'z': 15625, 'f': 125, 'q': 4096, 'u': 8000, 'n': 2197, 'm': 1728, 'r': 4913, 'k': 1000, 'g': 216, 'v': 9261}
Yes, it's possible. In python, Comprehension can be used in List, Set, Dictionary, etc.
You can write it this way
mydict = {k:v for (k,v) in blah}
Another detailed example of Dictionary Comprehension with the Conditional Statement and Loop:
parents = [father, mother]
parents = {parent:1 - P["mutation"] if parent in two_genes else 0.5 if parent in one_gene else P["mutation"] for parent in parents}
You can create a new dict for each pair and merge it with the previous dict:
reduce(lambda p, q: {**p, **{q[0]: q[1]}}, bla bla bla, {})
Obviously this approaches requires reduce from functools.
Assuming blah blah blah is a two-tuples list:
Let's see two methods:
# method 1
>>> lst = [('a', 2), ('b', 4), ('c', 6)]
>>> dict(lst)
{'a': 2, 'b': 4, 'c': 6}
# method 2
>>> lst = [('a', 2), ('b', 4), ('c', 6)]
>>> d = {k:v for k, v in lst}
>>> d
{'a': 2, 'b': 4, 'c': 6}
this approach uses iteration over the given date using a for loop.
Syntax: {key: value for (key, value) in data}
Eg:
# create a list comprehension with country and code:
Country_code = [('China', 86), ('USA', 1),
('Ghana', 233), ('Uk', 44)]
# use iterable method to show results
{key: value for (key, value) in Country_code}

Not getting the same result when inverting a dictionary twice

I'm trying to invert a simple dictionary like:
{'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
I'm using this function:
def invert(d):
return dict([(x,y) for y,x in d.iteritems()])
Now when I invert my dictionary, everything works out fine. When I invert it twice however, I get:
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
which is not in the same order as the dictionary I started with. Is there a problem with my invert function? Sorry I'm kinda new to python, but thanks for any help!
That is correct, dictionaries are unordered in python
from another so answer answer:
CPython implementation detail: Keys and values are listed in an
arbitrary order which is non-random, varies across Python
implementations, and depends on the dictionary’s history of insertions
and deletions.
from the docs:
It is best to think of a dictionary as an unordered set of key: value
pairs, with the requirement that the keys are unique (within one
dictionary). A pair of braces creates an empty dictionary: {}. Placing
a comma-separated list of key:value pairs within the braces adds
initial key:value pairs to the dictionary; this is also the way
dictionaries are written on output.
Python dictionaries are unsorted by design.
You can use collections.OrderedDict instead if you really need this behaviour.
Try running this code:
d = {
'a' : 1, 'b' : 2,
'c' : 3, 'd' : 4
}
def invert(d):
return dict([(x,y) for y,x in d.iteritems()])
print d
d = invert(d)
print d
d = invert(d)
print d
This is the output:
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
As you can see, it technically is the same dictionary, but when you declare it, it is unordered.

Categories

Resources