2012-10-20 27 views
18

Jaki jest najlepszy sposób na podzielenie słownika na pół?Podział słownika na 2 słowniki

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

Czekam na to zrobić:

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

To nie ma znaczenia, które klucze/wartości iść do każdego słownika. Po prostu szukam najprostszego sposobu dzielenia słownika na dwie części.

+2

Jaka jest twoja definicja * na dwie *, masz na myśli połowę kluczy w każdym słowniku? –

Odpowiedz

17

to będzie działać, chociaż nie testowałem krawędziowe przypadki:

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

Etui na krawędzie nie stanowią problemu; Przekroje poza zakresem po prostu zwracają puste listy, a wywołujący dykt na pustej liście zwraca pusty słownik. JEDNAK: http://docs.python.org/library/stdtypes.html#dict.items wydaje się wskazywać, że specyfikacja Pythona nie gwarantuje, że wywołania przedmiotów() zwrócą pary w tej samej kolejności za każdym razem! Być może, aby być teoretycznie poprawnym, powinniśmy przechowywać wynik wywołania items(), a następnie kroić ten zapisany wynik? –

+0

@MarkAmery Wierzę, że są gwarantowane, że będą stabilne, to znaczy, że powrócą w tej samej kolejności, o ile nic nie zmieni słownika, chociaż ta kolejność jest dowolna, więc powinno to wszystko naprawić. –

+0

@ Lattyware To, co właśnie powiedziałeś, jest wymienione w dokumentach jako prawdziwe w przypadku CPython. Przez pominięcie wynika, że ​​inne implementacje Pythona nie muszą teoretycznie tego gwarantować. Prawdę mówiąc, trudno sobie wyobrazić rozsądne wdrożenie, w którym tak nie było, więc jest to naprawdę problem teoretyczny ... –

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, ale '{klawisz: d [klawisz] dla i, klawisz wyliczyć (d)' itp. Byłoby jeszcze prostsze. – georg

5

oto sposób to zrobić przy użyciu iterację ciągu elementów w słowniku i 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

Możemy to zrobić skutecznie z itertools.zip_longest() (uwaga to itertools.izip_longest() w 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) 

co daje nam:

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

Dlaczego w tej sprawie? –

2

Jeśli używasz python +3.3 i chcesz, aby podzielony słowniki być taka sama w różnych wywołań Pythona, nie używaj .items, ponieważ hash wartościach kluczy, która określa kolejność .items() zmieni się między wywołaniami Pythona. Zobacz Hash randomization


1

Oto funkcja, która może być używana do dzielenia słownika żadnych podziałów.

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 nie działa dla mnie. Musiałem rzucić na listę, zanim mogłem indeksować wynik wywołania .items(). (ja działa Pyton 3.6 przykładzie)

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 pamiętać, że nie muszą być przechowywane w celu stworzenia tak wskaźniki mogą być mieszane.