2012-10-20 7 views
18

Каков наилучший способ разделить словарь пополам?Разделить словарь на 2 словаря

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

Я ищу, чтобы сделать это:

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

Это не имеет значения, какие ключи/значения идут в каждом словаре. Я просто ищу простейший способ разделить словарь на два.

+2

Каково ваше определение * в двух *, вы имеете в виду половину ключей в каждом словаре? –

ответ

17

Это будет работать, хотя я не проверял край-кейсы:

>>> 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} 
+2

Кромки не являются проблемой; внерезковые фрагменты просто возвращают пустые списки и вызов dict в пустом списке возвращает пустой словарь. ОДНАКО: http://docs.python.org/library/stdtypes.html#dict.items, похоже, указывает, что спецификация Python не гарантирует, что вызовы items() будут возвращать пары в одном порядке каждый раз! Возможно, чтобы быть теоретически правильным, мы должны сохранить результат вызова элементов(), а затем нарезать сохраненный результат? –

+0

@MarkAmery Я считаю, что они гарантированно будут стабильными, то есть они вернутся в том же порядке, если ничего не изменит словарь, хотя этот порядок произволен, так что это должно сделать все в порядке. –

+0

@Lattyware То, что вы только что сказали, указано в документах как истинное для CPython. Из упущения подразумевается, что другие реализации Python не должны, теоретически, гарантировать это. По общему признанию, трудно представить себе разумную реализацию, в которой это не так, так что это действительно теоретическая проблема только ... –

3
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} 
+1

+1, но '{key: d [key] для i, key enumerate (d)' и т. Д. Будет еще проще. – georg

5

Вот способ сделать это с помощью итератора по элементам в словаре и 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 
0

Мы можем сделать это эффективно с itertools.zip_longest() (заметьте это itertools.izip_longest() в 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) 

Что дает нам:

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

Почему нисходящий сигнал на этом? –

2

Если вы используете python +3.3, и хотите, чтобы ваши расщепленные словари быть одинаковыми в разных питона заклятий, не используйте .items, поскольку хэш-значения клавиш, которая определяет порядок .items() будет изменяться между вызовами python. См Hash randomization


1

Вот функция, которая может быть использована для разделения словаря любых подразделений.

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 
0

The answer by jone не работал для меня. Мне нужно было указать в список, прежде чем я смог проиндексировать результат вызова .items(). (Я бегу на Python 3.6 в примере)

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

обратите внимание на dicts не обязательно хранится в порядке создания так индексы могут быть перепутаны.

Смежные вопросы