Как я могу перебрать список словарей и объединить словари, чтобы сформировать новый более короткий список слов? - PullRequest
2 голосов
/ 17 февраля 2012

У меня есть список тарифов на рейсы авиакомпаний, в которых 'price', 'tickettype', указывает, является ли тариф «односторонним» (в отличие от поездки туда и обратно и отображается в другом списке рейсов с помощью целочисленного кода. Носписок, который я получаю, дублируется.

[
{'price' : 1800, 'oneway' : 1, 'inboundJourneys' : [], "outboundJourneys": [3], 'tickettypecode' : 'SDS'},
{'price' : 1800, 'oneway' : 1, 'inboundJourneys' : [9,10,11], "outboundJourneys": [], 'tickettypecode' : 'SDS'},
{'price' : 1800, 'oneway' : 1, 'inboundJourneys' : [14,16], "outboundJourneys": [], 'tickettypecode' : 'SDS'},
{'price' : '2300', 'oneway' : 1, 'inboundJourneys' : [], "outboundJourneys": [6,8,9], 'tickettypecode' : 'TAR'},
{'price' : 2300, 'oneway' : 1, 'inboundJourneys' : [12,13,14], "outboundJourneys": [3], 'tickettypecode' : 'TAR'},
{'price' : 900, 'oneway' : 1, 'inboundJourneys' : [], "outboundJourneys": [18,19,20], 'tickettypecode' : 'GED'},
{'price' : 900, 'oneway' : 1, 'inboundJourneys' : [14,16,17], "outboundJourneys": [], 'tickettypecode' : 'GED'},
{'price' : 1200, 'oneway' : 1, 'inboundJourneys' : [], "outboundJourneys": [25], 'tickettypecode' : 'ABC'},
{'price' : 1200, 'oneway' : 1, 'inboundJourneys' : [32], "outboundJourneys": [], 'tickettypecode' : 'ABC'}
]

Что мне нужно:

Где 'price' равно, 'tickettypecode' равно и 'oneway' равно, есть один словарь всписок так заканчивается:

[
{'price' : 1800, 'oneway' : 1, 'inboundJourneys' : [9,10,11,14,16], "outboundJourneys": [3], 'tickettypecode' : 'SDS'},
{'price' : 2300, 'oneway' : 1, 'inboundJourneys' : [12,13,14], "outboundJourneys": ['6,8,9'], 'tickettypecode' : 'TAR'},
{'price' : 900, 'oneway' : 1, 'inboundJourneys' : [14,16,17], "outboundJourneys": [18,19,20], 'tickettypecode' : 'GED'},
{'price' : 1200, 'oneway' : 1, 'inboundJourneys' : [32], "outboundJourneys": [25], 'tickettypecode' : 'ABC'}
]

Я перепробовал много подходов, но я в тупике.

Ответы [ 4 ]

3 голосов
/ 17 февраля 2012

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

merged = {}

for item in original:
    key = (item['price'], item['tickettypecode'], item['oneway'])
    if key in merged:
        for mergekey in ['inboundJourneys','outboundJourneys']:
            # assign extended copy rather than using list.extend()
            merged[key][mergekey] = merged[key][mergekey] + item[mergekey]
    else:
        merged[key] = item.copy()

mergedlist = merged.values()
0 голосов
/ 17 февраля 2012

Я бы сделал это так:

import copy

def merge(iterable, keys, update):
    merged = {}
    for d in iterable:
        merge_key = tuple(d[k] for k in keys)
        m = merged.get(merge_key)
        if m:
            for u in update:
                m[u].extend(d[u])
        else:
            merged[merge_key] = copy.deepcopy(d)

    return list(merged.values())  # list(dict_view)

Я проверю это на вашем экзамене:

keys = ('price','tickettypecode','oneway')
update = ('inboundJourneys','outboundJourneys')
merge(l, keys, update)

И я получил:

[{'inboundJourneys': [32],
  'oneway': 1,
  'outboundJourneys': [25],
  'price': 1200,
  'tickettypecode': 'ABC'},
 {'inboundJourneys': [12, 13, 14],
  'oneway': 1,
  'outboundJourneys': [6, 8, 9, 3],
  'price': 2300,
  'tickettypecode': 'TAR'},
 {'inboundJourneys': [9, 10, 11, 14, 16],
  'oneway': 1,
  'outboundJourneys': [3],
  'price': 1800,
  'tickettypecode': 'SDS'},
 {'inboundJourneys': [14, 16, 17],
  'oneway': 1,
  'outboundJourneys': [18, 19, 20],
  'price': 900,
  'tickettypecode': 'GED'}]
0 голосов
/ 17 февраля 2012

Ужасно неэффективное решение, но отправная точка:

answer = []
for myDict in myList:
    for d in answer:
        if d['oneway']==myDict['oneway'] and d['price']==myDict['price'] and d['tickettype']==myDict['tickettype']:
            break
    else:
        answer.append(myDict)

Надеюсь, это поможет

0 голосов
/ 17 февраля 2012

В общем, подобные ситуации лучше всего обрабатываются словарями. Например:

l = [(1, 2, 3), (1, 2, 8), (2, 3, 9), (5, 6, 66),  
     (3, 4, 22), (4, 5, 24), (5, 6, 55), (3, 4, 11)]

Здесь у нас есть список кортежей. Теперь предположим, что мы хотим, чтобы два кортежа были «равны», если первые два значения в кортеже равны, и мы хотим объединить более поздние значения. Мы можем использовать кортежи в качестве словарных ключей; поэтому для каждого кортежа мы генерируем кортеж ключа следующим образом. Для ясности я определю функцию здесь:

def get_key(tup):
    return tup[0:2]

Это нарезает кортеж, возвращая кортеж с первыми двумя значениями. Для такой простой операции функция может показаться излишней, но для более сложных операций она становится намного понятнее.

Я также определю функцию, которая возвращает дополнительные данные:

def get_extra(tup):
    return tup[2]

Теперь мы создаем словарь:

consolidated_tuples = {}

и заполните его:

for tup in l:
    key = get_key(tup)
    extra = get_extra(tup)
    if key not in consolidated_tuples:
        consolidated_tuples[key] = [extra]
    else:
        consolidated_tuples[key].append(extra)

Это просто проверяет, есть ли ключ в словаре. Если это не так, то он создает список, содержащий последнее значение в кортеже, и назначает этот список ключу. Если это так, то он добавляет последнее значение в данном кортеже к списку (который уже существует). Таким образом, дубликаты объединяются; кортежи, генерирующие один и тот же ключ, ведут к одному и тому же списку, который затем заполняется различными конечными значениями.

Вы можете легко расширить этот подход для работы со списком словарей; это просто становится немного сложнее.

Из этого базового кода мы можем добавить некоторую изощренность. Например, в словарях есть метод setdefault, который пытается получить доступ к ключу в словаре и, если это невозможно, создает его, присваивает ему значение по умолчанию и возвращает это значение по умолчанию. Это означает, что вышеприведенный оператор if... else может быть сжат:

for tup in l:
    consolidated_tuples.setdefault(get_key(tup), []).append(get_extra(tup))

Эквивалентный подход заключается в использовании defaultdict, который делает то же самое, что и выше, за кадром:

import collections
consolidated_tuples = collections.defaultdict(list)

Каждый раз, когда к несуществующему key обращаются, defaultdict вызывает list, связывает результат с key и возвращает полученный пустой список.

for tup in l:
    consolidated_tuples[get_key(tup)].append(get_extra(tup))

Все, что вам нужно сделать сейчас, это переписать get_key и get_extra, чтобы работать с данными выше.

>>> def get_key(d):
...     return (int(d['price']), d['oneway'], d['tickettypecode'])
... 
>>> def get_extra(d):
...     return (d['outboundJourneys'], d['inboundJourneys'])
... 
>>> merged_data = collections.defaultdict(list)
>>> for d in data:
...     merged_data[get_key(d)].append(get_extra(d))

Результат может быть легко преобразован в исходную структуру; если вы хотите включить 'price' и так далее в словари, просто добавьте их на следующем шаге:

>>> for k in merged_data:
...     ob, ib = zip(*merged_data[k])
...     merged_data[k] = {'outboundJourneys': [x for l in ob for x in l],
...                       'inboundJourneys': [x for l in ib for x in l]}
... 
>>> merged_data
defaultdict(<type 'list'>, {
    (2300, 1, 'TAR'): 
        {'outboundJourneys': [6, 8, 9, 3], 'inboundJourneys': [12, 13, 14]}, 
    (1200, 1, 'ABC'): {'outboundJourneys': [25], 'inboundJourneys': [32]}, 
    (1800, 1, 'SDS'): 
        {'outboundJourneys': [3], 'inboundJourneys': [9, 10, 11, 14, 16]}, 
    (900, 1, 'GED'): 
        {'outboundJourneys': [18, 19, 20], 'inboundJourneys': [14, 16, 17]}
})

Вы также можете написать функцию, которая, вместо простого добавления дополнительных данных в список, объединит их более сложным способом. В этом случае defaultdict, вероятно, добавляет немного ненужных усложнений; мы можем просто использовать dict.get(key, default), который ищет ключ и возвращает значение по умолчанию, если оно не найдено. Собираем все вместе, настроенные для данных выше (здесь с именем flights):

def merge_dict(d1, d2, key_names):
    merged_d = d1.copy()
    merged_d.update(d2)
    merged_d.update((k, d1.get(k, []) + d2.get(k, [])) for k in key_names)
    return merged_d

merged = {}        
for d in flights:
    key = (int(d['price']), d['tickettypecode'], d['oneway'])
    cd = merged.get(key, {})
    merged[key] = merge_dict(cd, d, ('inboundJourneys', 'outboundJourneys'))

Результат:

>>> consolidated_flights
{(1200, 'ABC', 1): {'inboundJourneys': [32], 'price': 1200, 
     'outboundJourneys': [25], 'oneway': 1, 'tickettypecode': 'ABC'}, 
 (2300, 'TAR', 1): {'inboundJourneys': [12, 13, 14], 'price': 2300, 
     'outboundJourneys': [6, 8, 9, 3], 'oneway': 1, 'tickettypecode': 'TAR'}, 
 (1800, 'SDS', 1): {'inboundJourneys': [9, 10, 11, 14, 16], 'price': 1800, 
     'outboundJourneys': [3], 'oneway': 1, 'tickettypecode': 'SDS'}, 
 (900, 'GED', 1): {'inboundJourneys': [14, 16, 17], 'price': 900, 
     'outboundJourneys': [18, 19, 20], 'oneway': 1, 'tickettypecode': 'GED'}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...