List of dicts to/from dict of lists

PythonListDictionary

Python Problem Overview


I am looking to change back and forth between a dictionary of lists (all of the same length):

DL = {'a': [0, 1], 'b': [2, 3]}

and a list of dictionaries:

LD = [{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]

I am looking for the cleanest way to switch between the two forms.

Python Solutions


Solution 1 - Python

For those of you that enjoy clever/hacky one-liners.

Here is DL to LD:

v = [dict(zip(DL,t)) for t in zip(*DL.values())]
print(v)

and LD to DL:

v = {k: [dic[k] for dic in LD] for k in LD[0]}
print(v)

LD to DL is a little hackier since you are assuming that the keys are the same in each dict. Also, please note that I do not condone the use of such code in any kind of real system.

Solution 2 - Python

Perhaps consider using numpy:

import numpy as np

arr = np.array([(0, 2), (1, 3)], dtype=[('a', int), ('b', int)])
print(arr)
# [(0, 2) (1, 3)]

Here we access columns indexed by names, e.g. 'a', or 'b' (sort of like DL):

print(arr['a'])
# [0 1]

Here we access rows by integer index (sort of like LD):

print(arr[0])
# (0, 2)

Each value in the row can be accessed by column name (sort of like LD):

print(arr[0]['b'])
# 2

Solution 3 - Python

If you're allowed to use outside packages, Pandas works great for this:

import pandas as pd
pd.DataFrame(DL).to_dict(orient="records")

Which outputs:

[{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]

You can also use orient="list" to get back the original structure

{'a': [0, 1], 'b': [2, 3]}

Solution 4 - Python

To go from the list of dictionaries, it is straightforward:

You can use this form:

DL={'a':[0,1],'b':[2,3], 'c':[4,5]}
LD=[{'a':0,'b':2, 'c':4},{'a':1,'b':3, 'c':5}]

nd={}
for d in LD:
    for k,v in d.items():
        try:
            nd[k].append(v)
        except KeyError:
            nd[k]=[v]
            
print nd     
#{'a': [0, 1], 'c': [4, 5], 'b': [2, 3]}

Or use defaultdict:

nd=cl.defaultdict(list)
for d in LD:
   for key,val in d.items():
      nd[key].append(val)
      
print dict(nd.items())
#{'a': [0, 1], 'c': [4, 5], 'b': [2, 3]}

Going the other way is problematic. You need to have some information of the insertion order into the list from keys from the dictionary. Recall that the order of keys in a dict is not necessarily the same as the original insertion order.

For giggles, assume the insertion order is based on sorted keys. You can then do it this way:

nl=[]
nl_index=[]

for k in sorted(DL.keys()):
    nl.append({k:[]})
    nl_index.append(k)
    
for key,l in DL.items():
    for item in l:
        nl[nl_index.index(key)][key].append(item)
        
print nl        
#[{'a': [0, 1]}, {'b': [2, 3]}, {'c': [4, 5]}]

If your question was based on curiosity, there is your answer. If you have a real-world problem, let me suggest you rethink your data structures. Neither of these seems to be a very scalable solution.

Solution 5 - Python

Here are the one-line solutions (spread out over multiple lines for readability) that I came up with:

if dl is your original dict of lists:

dl = {"a":[0, 1],"b":[2, 3]}

Then here's how to convert it to a list of dicts:

ld = [{key:value[index] for key,value in dl.items()}
         for index in range(max(map(len,dl.values())))]

Which, if you assume that all your lists are the same length, you can simplify and gain a performance increase by going to:

ld = [{key:value[index] for key, value in dl.items()}
        for index in range(len(dl.values()[0]))]

If dl contains unsymmetrical lists the following works fine:

from itertools import product

dl = {"a":[0, 1],"b":[2, 3, 4], "c":[5, 6, 7, 8]}

ld = [dict(zip(dl.keys(), items))         for items in product(*(dl.values()))]

Here's how to convert that back into a dict of lists:

dl2 = {key:[item[key] for item in ld]
         for key in list(functools.reduce(
             lambda x, y: x.union(y),
             (set(dicts.keys()) for dicts in ld)
         ))
      }

If you're using Python 2 instead of Python 3, you can just use reduce instead of functools.reduce there.

You can simplify this if you assume that all the dicts in your list will have the same keys:

dl2 = {key:[item[key] for item in ld] for key in ld[0].keys() }

Solution 6 - Python

cytoolz.dicttoolz.merge_with

Docs

from cytoolz.dicttoolz import merge_with

merge_with(list, *LD)

{'a': [0, 1], 'b': [2, 3]}

Non-cython version

Docs

from toolz.dicttoolz import merge_with

merge_with(list, *LD)

{'a': [0, 1], 'b': [2, 3]}

Solution 7 - Python

The python module of pandas can give you an easy-understanding solution. As a complement to @chiang's answer, the solutions of both D-to-L and L-to-D are as follows:

import pandas as pd
DL = {'a': [0, 1], 'b': [2, 3]}
out1 = pd.DataFrame(DL).to_dict('records')

Output:

[{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]

In the other direction:

LD = [{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]
out2 = pd.DataFrame(LD).to_dict('list')

Output:

{'a': [0, 1], 'b': [2, 3]}

Solution 8 - Python

Cleanest way I can think of a summer friday. As a bonus, it supports lists of different lengths (but in this case, DLtoLD(LDtoDL(l)) is no more identity).

  1. From list to dict

    Actually less clean than @dwerk's defaultdict version.

     def LDtoDL (l) :
        result = {}
        for d in l :
           for k, v in d.items() :
              result[k] = result.get(k,[]) + [v] #inefficient
        return result
    
  2. From dict to list

     def DLtoLD (d) :
        if not d :
           return []
        #reserve as much *distinct* dicts as the longest sequence
        result = [{} for i in range(max (map (len, d.values())))]
        #fill each dict, one key at a time
        for k, seq in d.items() :
           for oneDict, oneValue in zip(result, seq) :
          oneDict[k] = oneValue
        return result
    

Solution 9 - Python

Here my small script :

a = {'a': [0, 1], 'b': [2, 3]}
elem = {}
result = []

for i in a['a']: # (1)
    for key, value in a.items():
        elem[key] = value[i]
    result.append(elem)
    elem = {}

print result

I'm not sure that is the beautiful way.

(1) You suppose that you have the same length for the lists

Solution 10 - Python

Here is a solution without any libraries used:

def dl_to_ld(initial):
    finalList = []
    neededLen = 0

    for key in initial:
	    if(len(initial[key]) > neededLen):
		    neededLen = len(initial[key])

    for i in range(neededLen):
	    finalList.append({})

    for i in range(len(finalList)):
	    for key in initial:
		    try:
			    finalList[i][key] = initial[key][i]
		    except:
			    pass

    return finalList

You can call it as follows:

dl = {'a':[0,1],'b':[2,3]}
print(dl_to_ld(dl))

#[{'a': 0, 'b': 2}, {'a': 1, 'b': 3}]

Solution 11 - Python

If you don't mind a generator, you can use something like

def f(dl):
  l = list((k,v.__iter__()) for k,v in dl.items())
  while True:
    d = dict((k,i.next()) for k,i in l)
    if not d:
      break
    yield d

It's not as "clean" as it could be for Technical Reasons: My original implementation did yield dict(...), but this ends up being the empty dictionary because (in Python 2.5) a for b in c does not distinguish between a StopIteration exception when iterating over c and a StopIteration exception when evaluating a.

On the other hand, I can't work out what you're actually trying to do; it might be more sensible to design a data structure that meets your requirements instead of trying to shoehorn it in to the existing data structures. (For example, a list of dicts is a poor way to represent the result of a database query.)

Solution 12 - Python

I needed such a method which works for lists of different lengths (so this is a generalization of the original question). Since I did not find any code here that the way that I expected, here's my code which works for me:

def dict_of_lists_to_list_of_dicts(dict_of_lists: Dict[S, List[T]]) -> List[Dict[S, T]]:
    keys = list(dict_of_lists.keys())
    list_of_values = [dict_of_lists[key] for key in keys]
    product = list(itertools.product(*list_of_values))

    return [dict(zip(keys, product_elem)) for product_elem in product]

Examples:

>>> dict_of_lists_to_list_of_dicts({1: [3], 2: [4, 5]})
[{1: 3, 2: 4}, {1: 3, 2: 5}]
>>> dict_of_lists_to_list_of_dicts({1: [3, 4], 2: [5]})
[{1: 3, 2: 5}, {1: 4, 2: 5}]
>>> dict_of_lists_to_list_of_dicts({1: [3, 4], 2: [5, 6]})
[{1: 3, 2: 5}, {1: 3, 2: 6}, {1: 4, 2: 5}, {1: 4, 2: 6}]
>>> dict_of_lists_to_list_of_dicts({1: [3, 4], 2: [5, 6], 7: [8, 9, 10]})
[{1: 3, 2: 5, 7: 8},
 {1: 3, 2: 5, 7: 9},
 {1: 3, 2: 5, 7: 10},
 {1: 3, 2: 6, 7: 8},
 {1: 3, 2: 6, 7: 9},
 {1: 3, 2: 6, 7: 10},
 {1: 4, 2: 5, 7: 8},
 {1: 4, 2: 5, 7: 9},
 {1: 4, 2: 5, 7: 10},
 {1: 4, 2: 6, 7: 8},
 {1: 4, 2: 6, 7: 9},
 {1: 4, 2: 6, 7: 10}]

Solution 13 - Python

DL={'a':[0,1,2,3],'b':[2,3,4,5]}
LD=[{'a':0,'b':2},{'a':1,'b':3}]
Empty_list = []
Empty_dict = {}
# to find length of list in values of dictionry
len_list = 0
for i in DL.values():
    if len_list < len(i):
        len_list = len(i)

for k in range(len_list):        
    for i,j in DL.items():
        Empty_dict[i] = j[k]
    Empty_list.append(Empty_dict)
    Empty_dict = {}
LD = Empty_list

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAdam GreenhallView Question on Stackoverflow
Solution 1 - PythonAndrew FlorenView Answer on Stackoverflow
Solution 2 - PythonunutbuView Answer on Stackoverflow
Solution 3 - Pythoneric chiangView Answer on Stackoverflow
Solution 4 - PythondawgView Answer on Stackoverflow
Solution 5 - PythonAndrew ChenView Answer on Stackoverflow
Solution 6 - PythonpiRSquaredView Answer on Stackoverflow
Solution 7 - PythonSON_7093436View Answer on Stackoverflow
Solution 8 - PythonYvesgereYView Answer on Stackoverflow
Solution 9 - PythonSandro MundaView Answer on Stackoverflow
Solution 10 - PythonL. MayerView Answer on Stackoverflow
Solution 11 - Pythontc.View Answer on Stackoverflow
Solution 12 - PythondsteinhoefelView Answer on Stackoverflow
Solution 13 - PythonAnup SrujanView Answer on Stackoverflow