如何迭代字典列表并合并字典以形成新的更短的字典列表?

时间:2012-02-17 17:18:22

标签: python list dictionary merge

我有一张航空公司航班票价清单,其中'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 个答案:

答案 0 :(得分:3)

假设合并列表中的项目顺序无关紧要,只需浏览列表中的每个项目,如果您之前没有看过它就复制它,或者如果有的话合并字段。

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

答案 1 :(得分:0)

一般来说,这样的情况最好由字典处理。例如:

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_keyget_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'}}

答案 2 :(得分:0)

极其低效的解决方案,但是一个起点:

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)

希望这有帮助

答案 3 :(得分:0)

我会这样做:

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'}]