2016-03-22 1 views
3

Я хочу создать структуру данных с вложенными словарями и дублирующими ключами. Подробный пример:Как создать вложенные словари с дублирующимися ключами в python

data['State1']['Landon']['abc Area'] = 'BOB' 
data['State1']['Landon']['abc Area'] = 'SAM' 
data['State1']['Landon']['xyz Area'] = 'John' 
data['State2']['New York']['hjk Area'] = 'Ricky' 

for z in data['State1'].keys() , 
# I should get list ['Landon', 'Landon', 'Landon'] 
for y in data['State1']['Landon'].keys() , 
# I should get list ['abc Area', 'abc Area', 'xyz Area'] 

В настоящее время для хранения данных я использовал дополнительный счетчик ключ

data = Autovivification() 
data[state][city][area][counter] = ID 

Но при анализе общих записей (дубликаты, а) из города/региона, я должен использовать вложенное петли до счетчика.

for city in data['State1'].keys(): 
    for area in data['State1'][city].keys(): 
    for counter in data['State1'][city][area].keys(): 
    for temp in data['State1'][city][area][counter].values(): 
     cityList.append(city) 
     areaList.append(area) 

Для вложенных словарей, я нашел следующий код, отправленный nosklo

class AutoVivification(dict): 
    """Implementation of perl's autovivification feature.""" 
    def __getitem__(self, item):     
     try: 
      return dict.__getitem__(self, item) 
     except KeyError: 
      value = self[item] = type(self)() 
      return value 

и словарь с повторяющимися ключами, я нашел код, отправленный Scorpil

class Dictlist(dict): 
    def __setitem__(self, key, value): 
     try: 
      self[key] 
     except KeyError: 
      super(Dictlist, self).__setitem__(key, []) 
     self[key].append(value) 

как объединить Autovivification и Дублировать код класса? или существует ли какой-либо другой питонический способ обработки такого сценария?

+2

Можете ли вы объяснить, почему вам нужно дубликаты ключей? Это нормально, если будет отображаться «Area», где вы можете добавить несколько идентификаторов? –

+0

@germn: Я отредактировал вопрос с большим количеством объяснений, можете ли вы проверить – Aniketan

+0

Я обновил ответ, добавив новую функцию 'items_in', которая вернет вам список для данных. Я уверен, что было бы проще и понятнее реализовать функции для работы с структурой dict/list, а затем попытаться реализовать вашу новую структуру. –

ответ

3

Еще один пример использования defaultdict:

from collections import defaultdict 


data = defaultdict( # State 
    lambda: defaultdict( # City 
     lambda: defaultdict(list) # Area 
    ) 
) 


data['State']['City']['Area'].append('area 1') 
data['State']['City']['Area'].append('area 2') 
data['State']['City']['Area'].append('area 2') 


areas = data['State']['City']['Area'] 
print(areas) # ['area 1', 'area 2', 'area 2'] 

total = len(areas) 
print(total) # 3 

Как получить список элементов, которые вы хотите, с помощью этого решения:

data['State1']['Landon']['abc Area'].append('BOB') 
data['State1']['Landon']['abc Area'].append('SAM') 
data['State1']['Landon']['xyz Area'].append('John') 
data['State2']['New York']['hjk Area'].append('Ricky') 


def items_in(d): 
    res = [] 
    if isinstance(d, list): 
     res.extend(d) 
    elif isinstance(d, dict): 
     for k, v in d.items(): 
      res.extend([k] * len(items_in(v))) 
    else: 
     raise ValueError('Unknown data') 
    return res 


print(items_in(data['State1'])) # ['Landon', 'Landon', 'Landon'] 
print(items_in(data['State1']['Landon'])) # ['xyz Area', 'abc Area', 'abc Area'] 
print(items_in(data['State1']['Landon']['abc Area'])) # ['BOB', 'SAM'] 
print(items_in(data['State1']['Landon']['xyz Area'])) # ['John'] 

print(items_in(data['State2'])) # ['New York'] 
print(items_in(data['State2']['New York'])) # ['hjk Area'] 
1

Один простой способ будет сделать это список, а затем просто добавить каждый новый ключ к списку:

Data['State']['City']['Area'] = [] 
Data['State']['City']['Area'].append(ID) 
0

Вы могли бы заменить AutoVivication класс с одним, который автоматически vivificates Dictlists вместо dicts:

class AutoVivificationDL(Dictlist): 
    """Implementation of perl's autovivification feature.""" 
    def __getitem__(self, item):     
     try: 
      return dict.__getitem__(self, item) 
     except KeyError: 
      value = self[item] = type(self)() 
      return value 
+0

Спасибо, я отредактировал вопрос с большим количеством объяснений, не могли бы вы проверить? , когда я использовал ваш код «RuntimeError: максимальная глубина рекурсии, превышаемая при вызове объекта Python». – Aniketan

+0

Хм, не думал об этом. Я думаю, что это Python, говорящий вам, что вся эта вещь AutoVivification не Pythonic :) Возможно, вам лучше использовать комбинацию 'defaultdict' и обычных списков вместо« дублированных записей ».Если вы еще этого не сделали, вы также можете попытаться пересмотреть, нужна ли вам такая сверхгибкая структура данных, или вы заранее знаете о своих данных, чтобы просто построить вложенный dict с заданной глубиной. – egpbos

0
Data = {} 


values = [ 
    dict(State="CA", City="San Francisco", Area="North", Id="customer1"), 
    dict(State="CA", City="San Francisco", Area="Embarcadero", Id="customer1"), 
    dict(State="CA", City="San Francisco", Area="North", Id="customer2"), 

] 

for v in values: 
    #grab the existing entry. if it doesn't exist, returns a list 
    li = Data.setdefault((v["State"],v["City"],v["Area"]),[]) 
    li.append(v["Id"]) 

print "Data:%s" % (Data) 

выход:

Data:{('CA', 'San Francisco', 'North'): ['customer1', 'customer2'], ('CA', 'San Francisco', 'Embarcadero'): ['customer1']} 

Вы не ограничены очень простым значением Id, вы можете добавить почти все, что хотите в список. Если вы планируете делать это в нескольких местах, посмотрите на https://docs.python.org/2/library/collections.html#collections.defaultdict, который имеет встроенный setdefault.

Фактически вы можете добавлять идентификаторы в словарь вместо списка, это все равно.