Split a dictionary in half?

Question:

What is the best way to split a dictionary in half?

d = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}

I’m looking to do this:

d1 = {'key1': 1, 'key2': 2, 'key3': 3}
d2 = {'key4': 4, 'key5': 5}

It does not matter which keys/values go into each dictionary. I am simply looking for the simplest way to divide a dictionary into two.

Asked By: user1728853

||

Answers:

d1 = {key: value for i, (key, value) in enumerate(d.viewitems()) if i % 2 == 0}
d2 = {key: value for i, (key, value) in enumerate(d.viewitems()) if i % 2 == 1}
Answered By: Johan Råde

This would work, although I didn’t test edge-cases:

>>> d = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}
>>> d1 = dict(d.items()[len(d)/2:])
>>> d2 = dict(d.items()[:len(d)/2])
>>> print d1
{'key1': 1, 'key5': 5, 'key4': 4}
>>> print d2
{'key3': 3, 'key2': 2}

In python3:

d = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}
d1 = dict(list(d.items())[len(d)//2:])
d2 = dict(list(d.items())[:len(d)//2])

Also note that order of items is not guaranteed

Answered By: jone

Here’s a way to do it using an iterator over the items in the dictionary and itertools.islice:

import itertools

def splitDict(d):
    n = len(d) // 2          # length of smaller half
    i = iter(d.items())      # alternatively, i = d.iteritems() works in Python 2

    d1 = dict(itertools.islice(i, n))   # grab first n items
    d2 = dict(i)                        # grab the rest

    return d1, d2
Answered By: Blckknght

We can do this efficiently with itertools.zip_longest() (note this is itertools.izip_longest() in 2.x):

from itertools import zip_longest
d = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}
items1, items2 = zip(*zip_longest(*[iter(d.items())]*2))
d1 = dict(item for item in items1 if item is not None)
d2 = dict(item for item in items2 if item is not None)

Which gives us:

>>> d1
{'key3': 3, 'key1': 1, 'key4': 4}
>>> d2
{'key2': 2, 'key5': 5}
Answered By: Gareth Latty

If you use python +3.3, and want your splitted dictionaries to be the same across different python invocations, do not use .items, since the hash-values of the keys, which determines the order of .items() will change between python invocations.
See Hash randomization


Answered By: David Bielen

Here is the function which can be used to split a dictionary to any divisions.

import math

def linch_dict_divider(raw_dict, num):
    list_result = []
    len_raw_dict = len(raw_dict)
    if len_raw_dict > num:
        base_num = len_raw_dict / num
        addr_num = len_raw_dict % num
        for i in range(num):
            this_dict = dict()
            keys = list()
            if addr_num > 0:
                keys = raw_dict.keys()[:base_num + 1]
                addr_num -= 1
            else:
                keys = raw_dict.keys()[:base_num]
            for key in keys:
                this_dict[key] = raw_dict[key]
                del raw_dict[key]
            list_result.append(this_dict)

    else:
        for d in raw_dict:
            this_dict = dict()
            this_dict[d] = raw_dict[d]
            list_result.append(this_dict)

    return list_result

myDict = {'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5}
print myDict
myList = linch_dict_divider(myDict, 2)
print myList
Answered By: Joe Cheng

The answer by jone did not work for me. I had to cast to a list before I could index the result of the .items() call. (I am running Python 3.6 in the example)

d = {'one':1, 'two':2, 'three':3, 'four':4, 'five':5}
split_idx = 3
d1 = dict(list(d.items())[:split_idx])
d2 = dict(list(d.items())[split_idx:])

"""
output:
d1
{'one': 1, 'three': 3, 'two': 2}
d2
{'five': 5, 'four': 4}
"""

Note the dicts are not necessarily stored in the order of creation so the indexes may be mixed up.

Answered By: user3548798

If you used numpy, then you could do this :

def divide_dict(dictionary, chunk_size):

'''
Divide one dictionary into several dictionaries

Return a list, each item is a dictionary
'''

import numpy, collections

count_ar = numpy.linspace(0, len(dictionary), chunk_size+1, dtype= int)
group_lst = []
temp_dict = collections.defaultdict(lambda : None)
i = 1
for key, value in dictionary.items():
    temp_dict[key] = value
    if i in count_ar:
        group_lst.append(temp_dict)
        temp_dict = collections.defaultdict(lambda : None)
    i += 1
return group_lst
Answered By: woodword

Here’s a function that I use in Python 3.8 that can split a dict into a list containing the desired number of parts. If you specify more parts than elements, you’ll get some empty dicts in the resulting list.

def split_dict(input_dict: dict, num_parts: int) -> list:
    list_len: int = len(input_dict)
    return [dict(list(input_dict.items())[i * list_len // num_parts:(i + 1) * list_len // num_parts])
        for i in range(num_parts)]

Output:

>>> d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
>>> split_dict(d, 2)
[{'a': 1, 'b': 2}, {'c': 3, 'd': 4, 'e': 5}]
>>> split_dict(d, 3)
[{'a': 1}, {'b': 2, 'c': 3}, {'d': 4, 'e': 5}]
>>> split_dict(d, 7)
[{}, {'a': 1}, {'b': 2}, {}, {'c': 3}, {'d': 4}, {'e': 5}]
Answered By: Gethin LW
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.