2013-08-10 3 views
0

У меня есть список dicts который выглядит следующим образом:Группировка данных в списке из dicts

[{TYPE, OBJECT_ID, ACTOR, EXTRA_FIELDS}, ...] 

Я хотел бы пройти и агрегировать дубликаты {TYPE, OBJECT_ID} и сделать актер список так:

начать с:

[ {'type': 'LOVE', 'obj_id': 1242, 'actor': 'bob', {..}}, 
     {'type': 'LOVE', 'obj_id': 1242, 'actor': 'dave', {..}}, 
     {'type': 'FAV', 'obj_id': 1242, 'actor': 'sam', {..}}, 
     {'type': 'LOVE', 'obj_id': 242, 'actor': 'bob', {..}}] 

конец с:

[ {'type': 'LOVE', 'obj_id': 1242, 'actor': ['bob', 'dave'], {..}}, 
     {'type': 'FAV', 'obj_id': 1242, 'actor': ['sam'], {...}}, 
     {'type': 'LOVE', 'obj_id': 242, 'actor': ['bob'], {...}} ] 

EXTRA_FIELDS не нужно объединять, они могут просто использовать данные из одного из агрегированных элементов.

Как это сделать в python?

+4

Эти объектные литералы не имеют смысл, они заданы литералами. '{a, b}' даст (предполагается, что определены a и b), 'set ([a, b])'. – FakeRainBrigand

+4

Опубликовать некоторые реальные данные. –

+0

Ваш синтаксис очень незначителен, например, что есть {TYPE, OBJECT_ID, ACTOR, EXTRA_FIELDS}. В Python его набор, но я подозреваю, что вы не имеете в виду так? –

ответ

0

Вот как я это сделать:

def merge_dicts(list_of_dicts): 
    lookup = {} 
    results = [] 
    for d in list_of_dicts: 
     key = (d['type'], d['obj_id']) 
     try: # it's easier to ask forgiveness than permission 
      lookup[key]['actor'].append(d['actor']) 
     except KeyError: 
      val = {'type': d['type'], 
        'obj_id': d['obj_id'], 
        'actor': [d['actor']], # note, extra [] around value to make it a list 
        'extra_fields': d['extra_fields']} 
      lookup[key] = val 
      results.append(val) 

    return results 

В lookup Dict карты из кортежа из ключевых значений в словарях, которые были включены в список результатов. Эти выходные словари будут иметь значение actor, мутированное, если позже появятся другие словари с одним и тем же ключом.

Скорее, более естественным решением было бы избавиться от структуры данных из списка словарей и вместо этого перейти на один словарь, который отображает от type, obj_id ключей к значениям actors, extra_fields. Вот что это будет выглядеть следующим образом:

def merge_dicts2(list_of_dicts): 
    results = {} 
    for d in list_of_dicts: 
     key = (d['type'], d['obj_id']) 
     try: 
      results[key][0].append(d['actor']) 
     except KeyError: 
      results[key] = ([d['actor']], d['extra_fields']) 

    return results 

Это большая часть данных, что список dicts был, только порядок был потерян (и так как вы сливались элементы из старого списка, некоторые из этого порядка было будет потеряно независимо).

Если вы собираетесь быть итерация коллекции позже, этот способ гораздо проще, так как вы можете распаковать кортежи (даже вложенные из них) прямо в цикле:

combined_dict = merge_dicts(list_of_dicts) 

for (type, obj_id), (actors, extra_fields) in combined_dict.items(): 
    # do stuff with type, obj_id, actors, extra_fields 
0

Предполагая, что input список кортежей (не комплектов), что о

TYPE= 0 
OBJECT_ID= 1 
ACTOR= 2 
EXTRA_INFO= 3 
keys= set([ (e[TYPE] , e[OBJECT_ID]) for e in input ]) 
output= { k: [ (e[ACTOR] , e[EXTRA_INFO]) for e in input if (e[TYPE] , e[OBJECT_ID]) == k ] for k in keys } 

Или, если вы хотите остротами:

output= { k: [ (e[2] , e[3]) for e in input if (e[0] , e[1]) == k ] for k in [ (e[0] , e[1]) for e in input ] } 

Предполагая, что input список словарей это становится:

keys= set([ (e['type'] , e['obj_id']) for e in input ]) 
output= { k: [ { 'actor':e['actor'] , 'extra_info':e['extra_info'] } for e in input if (e['type'] , e['obj_id']) == k ] for k in keys } 

Или

output= { k: [ { 'actor':e['actor'] , 'extra_info':e['extra_info'] } for e in input if (e['type'] , e['obj_id']) == k ] for k in [ (e['type'] , e['obj_id']) for e in input ] } 

Конечно, вы также можете вручную написать, что это такое, но я бы не рекомендовал его, если объем данных слишком велик, и вы начинаете испытывать проблемы с производительностью, требующие оптимизации на низком уровне.

0

Ваш список Я обозначен как alist.

actors = {} 
extra = {} 
for x in alist: 
    if actors.has_key([(x['type'],x['obj_id'])): 
     actors[x['type'],x['obj_id']].append(x['actor']) 
    else: 
     actors[x['type'],x['obj_id']] = [] 
    extra[x['type'],x['obj_id']] = x['extra'] 

outlist = [] 
for k in actors.keys(): 
    x = {} 
    x['type'], x['obj_id'], x['actor'], x['extra'] = k[0], k[1], actors[k], extra[k] 
    outlist.append(x) 

outlist - это список результатов.

0

Вы должны переломить проблему в ее составные части.

Первое, что вам нужно сделать, это изменить все эти актеры в списках:

for dict in list_of_dicts: 
    dict['actor'] = [dict['actor']] 

Затем вам нужно написать метод, который проверяет, является ли конкретная пара находится в списке dicts, возвращая индекс, если он является:

def check_pair(list_of_dicts,type,obj_id): 
    #return index of matching pair, None otherwise 
    index = -1 
    for dict in list_of_dicts: 
    index += 1 
     if dict['type'] == type and dict['obj_id'] == obj_id: 
     return index 
    else: 
     return None 

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

new_list = [] 
for dict in list_of_dicts: 
    j = check_pair(new_list,dict['type'],dict['obj_id']) 
if j == None: 
    new_list.append(dict) 
else: 
    new_list[j]['actor'].append(dict['actor']) 

Я должен отметить, что, имея список dicts, как это довольно нетрадиционный материал, вы действительно должны выяснить способ сделать ваши структуры данных более разумным.

-2

Одним из решений является: во-первых, получить набор идентификаторов (набор уникальных комбинаций типа и obj_id); затем получить список участников для каждой комбинации.

identifiers = set((item['type'], item['obj_id']) for item in input_list) 
output_list = [] 
for type, obj_id in identifiers: 
    output_list.append({ 
     'type': type, 
     'obj_id': obj_id, 
     'actor': [item['actor'] for item in input_list 
      if item['type'] is type and item['obj_id'] is obj_id] 
     }) 

В качестве альтернативы, использовать кортежей в качестве словаря ключей:

actors_dict = {} 
for item in input_list: 
    actors_dict.setdefault((item['type'], item['obj_id']), []).append(item['actor']) 
output_list = [{'type': type, 'obj_id': obj_id, 'actor': actors} 
    for (type, obj_id), actors in actors_dict.iteritems()]  

Или более гибкий способ написания этого (например, в случае, если добавить другие значения, которые будут объединены) будет:

output_dict = {} 
for item in input_list: 
    k = item['type'], item['obj_id'] 
    if k in output_dict: 
     output_dict[k]['actor'].append(item['actor']) 
    else: 
     item['actor'] = [item['actor']] 
     output_dict[k] = item 
output_list = output_dict.values() 

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

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