最有效的方法是将两个嵌套列表压缩成一个单层字典

6
例如:

举个例子:

list1=['k1','k2','k3',['k4','k5',['k6','k7']]]
list2=['v1','v2','v3',['v4','v5',['v6','v7']]]

我希望您能将它们合并成一个词典,像这样:
{}
dict1={'k1':'v1','k2':'v2','k3':'v3','k4':'v4','k5':'v5','k6':'v6','k7':'v7'}

我有一种方法可以做到这一点,但我认为它需要太多时间:

def mergeToDict(keyList, valueList):
    resultDict = {}
    for key, value in itertools.izip(keyList, valueList):
        if type(key) == list and type(value) == list:
            resultDict=dict(resultDict,**mergeToDict(key, value))
        elif type(key) != list and type(key) != dict and type(key) != tuple:
            resultDict[key] = value
    return resultDict

有没有更好的想法?

你的解决方案看起来比下面所有的答案都要好。 - jterrace
4个回答

5

我会使用某种压平函数:

def flatten(it):
    if isinstance(it, str):
        yield it
        return
    try:
        for x in it:
            for y in flatten(x):
                yield y
    except TypeError:
        yield it

现在您可以执行以下操作

from itertools import izip
my_dict = dict(izip(flatten(list1), flatten(list2)))

我认为这种方式更加通用和透明,有利于读者理解。

我测试了你的代码,但似乎它运行的速度比我的代码慢了两倍。 - BackMountainBird
3
flatten() 函数有优化的空间 -- 例如,可以使用 isinstance(it, collections.Iterable)hasattr(it, "__iter__") 避免抛出异常,或者仅降入类型为 list 的可迭代对象。这些更改很可能会牺牲通用性或易读性以换取一点点性能提升。如果性能真的是一个问题,我怀疑这不是优化的正确地方 -- 而应该从一开始就改变这些列表的创建方式。无论如何,要经过分析确保你在正确的地方进行了优化。 - Sven Marnach

1

假设已经定义了flatten函数:

>>> def flatten(l):
...     r = []
...     for x in l:
...             if isinstance(x, list):
...                     r.extend(flatten(x))
...             else:
...                     r.append(x)
...     return r

dict(zip(flatten(list1), flatten(list2))) 看起来与你的代码一样快。而且这是更方便的方法,正如其他人所说。


1

如果你只有这种情况(嵌套列表但形状相同),我认为你根本不需要扁平化。以下是一种方法,它在我的计算机上至少比你的快2-3倍(再次强调,仅适用于这种限制):

def appendDict(list1, list2, resultDict):
    for idx, val in enumerate(list1):
        if isinstance(val, list):       
            appendDict(val, list2[idx], resultDict)
        else:
            resultDict[val] = list2[idx]

list1=['k1','k2','k3',['k4','k5',['k6','k7']]]
list2=['v1','v2','v3',['v4','v5',['v6','v7']]]
resultDict = {}
appendDict(list1, list2, resultDict)
print resultDict

{'k3': 'v3', 'k2': 'v2', 'k1': 'v1', 'k7': 'v7', 'k6': 'v6', 'k5': 'v5', 'k4': 'v4'}

不同方法的比较:

OP的方法,运行10000次:0.290050983429

其他推荐的方法,运行10000次:0.580717086792

这种方法,在10000次运行中:0.155267000198

也许它不像其他解决方案那样优雅,但在这里性能似乎是主要问题。


2
全局变量同样可以作为参数传递给函数,其思想是仅对其中一个列表进行迭代,并仅执行一次。 - Bogdan
那真的很快。也许你可以将 "resultDict" 作为一个参数传入,这看起来更好。 - BackMountainBird

0

我喜欢堆栈和生成器函数:

def flatten(seq, *seq_types):
    stack = [iter(seq)]
    while stack:
        for item in stack[-1]:
            if isinstance(item, seq_types):
                stack.append(iter(item))
                break
            else:
                yield item
        else:
            stack.pop()

keys = [0, 1, (2, 3, [4.])]
values = (5, 6, (7, "joe", [9]))
print dict(zip(flatten(keys, list, tuple), flatten(values, tuple, list)))

结果:

{0: 5, 1: 6, 2: 7, 3: 'joe', 4.0: 9}

或者,如果您确定输入列表具有相同的结构,这也可能起作用:

def flatten(seq, *seq_types):
    seq = list(seq)
    for item in seq:
        if isinstance(item, seq_types):
            seq.extend(item)
        else:
            yield item

请注意,项目的顺序可能会改变:

print list(flatten([1, 2, [3, 4, [5]]], list))
print list(flatten([1, 2, [[3, 4], 5]], list))

结果:

[1, 2, 3, 4, 5]
[1, 2, 5, 3, 4]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接