在嵌套列表推导中仅使用一个列表中的项目

时间:2015-11-29 22:06:42

标签: python list for-loop nested list-comprehension

我正在尝试使用列表推导来生成一个新列表,该列表包含从list1直接跟随(冒号后)的字母,其中list2中的单词以该特定字母开头。我设法使用嵌套for循环对此进行编码,如下所示:

list1=["A","B"]
list2=["Apple","Banana","Balloon","Boxer","Crayons","Elephant"]

newlist=[]
for i in list1:
    newlist.append(i+":")
    for j in list2:
        if j[0]==i:
            newlist[-1]+=j+","

产生预期结果:['A:Apple,', 'B:Banana,Balloon,Boxer,']

尝试使用列表理解相同,我想出了以下内容:

list1=["A","B"]
list2=["Apple","Banana","Balloon","Boxer","Crayons","Elephant"]

newlist=[i+":"+j+"," for i in list1 for j in list2 if i==j[0]]

导致:['A:Apple,', 'B:Banana,', 'B:Balloon,', 'B:Boxer,']

每次找到带有该首字母的单词时,会在newlist中创建一个新项目,而我的意图是每个字母有一个项目。

有没有办法编辑列表推导代码以获得与使用嵌套for循环相同的结果?

2 个答案:

答案 0 :(得分:2)

您需要做的就是删除第二个for循环并将其替换为','.join(matching_words)调用,现在您在字符串连接中使用j

newlist = ['{}:{}'.format(l, ','.join([w for w in list2 if w[0] == l])) for l in list1]

这不是非常有效;你为每个字母循环所有 list2中的单词。为了有效地执行此操作,您最好将列表预处理为字典:

list2_map = {}
for word in list2:
    list2_map.setdefault(word[0], []).append(word)

newlist = ['{}:{}'.format(l, ','.join(list2_map.get(l, []))) for l in list1]

第一个循环构建一个字典,将首字母映射到单词列表,这样您就可以直接使用这些列表而不是使用嵌套列表理解。

演示:

>>> list1 = ['A', 'B']
>>> list2 = ['Apple', 'Banana', 'Balloon', 'Boxer', 'Crayons', 'Elephant']
>>> list2_map = {}
>>> for word in list2:
...     list2_map.setdefault(word[0], []).append(word)
...
>>> ['{}:{}'.format(l, ','.join(list2_map.get(l, []))) for l in list1]
['A:Apple', 'B:Banana,Balloon,Boxer']

上述算法在所有list2中循环两次,在list1之后循环两次,使其成为O(N)线性算法(将单个单词添加到list2或单个字母以list1增加固定金额的时间量。对于list2中的每个字母,您的版本会在list1上循环一次,使其成为O(NM)算法,每当您添加字母或单词时,都会增加指数级别的时间。

要将其添加到数字中,如果您展开list1以涵盖所有26个ASCII大写字母并展开list2以包含1000个字,那么您的方法(扫描所有list2的单词给定的字母)会做26000步。我的版本,包括预先构建地图,只需要2026步。如果list2包含100万字,则您的版本必须生成2600万个步骤,耗费200万个和26个。

答案 1 :(得分:2)

list1=["A","B"]
list2=["Apple","Banana","Balloon","Boxer","Crayons","Elephant"]

res = [l1 + ':' + ','.join(l2 for l2 in list2 if l2.startswith(l1)) for l1 in list1]
print(res)

# ['A:Apple', 'B:Banana,Balloon,Boxer']

但是阅读起来似乎很复杂,所以我建议使用嵌套循环。您可以创建生成器以提高可读性(如果您认为此版本更具可读性):

def f(list1, list2):
    for l1 in list1:
        val = ','.join(l2 for l2 in list2 if l2.startswith(l1))
        yield l1 + ':' + val

print(list(f(list1, list2)))

# ['A:Apple', 'B:Banana,Balloon,Boxer']