В общем, подобные ситуации лучше всего обрабатываются словарями. Например:
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'}}