Pythonic方式生成列表列表

时间:2016-06-01 09:36:30

标签: python

从以下输入实现转换的最pythonic方式是什么:

 input = [('a', 1), ('a', 10), ('b', 244), ('c', 31) , ('c',45)]

到所需的输出:

output = [[('a',1),('a',10)],[('c',31),('c',45)]]

我在其中列出了具有相同第一个元素的元组。

感觉Python在一行中编写复杂的东西时具有很强的潜力(我是新的),我决定使用综合列表。我最初的尝试是这样的:

output = [x for x in input if [k[0] for f in input].count(x[0])>1] 

给我一​​个很好的清单,列出我所有的"伪"重复:

output = [('a',1),('a',10),('c',31),('c',45)]

我进一步处理以获得我的结果。

我的问题是:有没有办法使用综合列表而不是两个(丑陋)步骤在一行中实现此结果?

5 个答案:

答案 0 :(得分:4)

使用groupby中的itertools和列表理解。这将给你一个简单的一个班轮:

from itertools import groupby

filter(lambda x: len(x)>1, [list(g) for i,g in groupby(input, key=lambda x: x[0])])
[[('a', 1), ('a', 10)], [('c', 31), ('c', 45)]]

答案 1 :(得分:3)

使用1-liner列表理解:

>>> L=[('a', 1), ('a', 10), ('b', 244), ('c', 31) , ('c',45)]
>>> [list(filter(lambda x:x[0]==i, L)) for i in set(map(lambda x:x[0], L)) if len(list(filter(lambda x:x[0]==i, L)))>1]
[[('a', 1), ('a', 10)], [('c', 31), ('c', 45)]]

答案 2 :(得分:2)

使用itertools.groupby。我的解决方案不是单行,而是更具可读性。

import itertools

lists_in = [('a', 1), ('a', 10), ('b', 244), ('c', 31) , ('c',45)]

lists_out = list()
for name, group in itertools.groupby(lists_in, key=lambda x:x[0]):
    l = list(group)
    if len(l) == 2:
        lists_out.extend(l)

print(lists_out)
# Output
[('a', 1), ('a', 10), ('c', 31), ('c', 45)]

答案 3 :(得分:1)

以下内容没有错:

input = [('a', 1), ('a', 10), ('b', 244), ('c', 31) , ('c',45)]

d = {}
for i in input:
  if i[0] in d:
    d[i[0]].append(i)
  else:
    d[i[0]] = [i]

print([d[k] for k in d if len(d[k]) > 1])

不要忘记,你必须在可读性和聪明性之间保持平衡。

稍后编辑:我实际上从其他答案中收集了其他解决方案并测量了时间执行(200000个均匀分布的元组,其中'a' - 'z'第一个元素),见下文:

# 0.048532 s
def foo(input):
  d = {}
  for i in input:
    if i[0] in d:
      d[i[0]].append(i)
    else:
      d[i[0]] = [i]

  return len(([d[k] for k in d if len(d[k]) > 1]))

# 1.9594 s
def foo2(input):
  [list(filter(lambda x:x[0]==i, input)) for i in set(map(lambda x:x[0], input)) if len(list(filter(lambda x:x[0]==i, input)))>1]

# 0.209639 s
def foo3(input):
  [filter(lambda x: len(x)>1, [list(g) for i,g in itertools.groupby(input, key=lambda x: x[0])])]

# 0.188625
def foo4(input):
  lists = list()
  for name, group in itertools.groupby(input, key=lambda x: x[0]):
    l = list(group)
    if len(l) == 2:
      lists.extend(l)

# didn't even finish, >120 s
def foo5(input_list):
  [[x for x in input_list if x[0]==a] for a in {x[0] for x in input_list if [k[0] for k in input].count(x[0])>1}]

所以是的,更聪明的单线解决方案,但更慢更难阅读并不是真正的“最pythonic”。

答案 4 :(得分:1)

这是一个解决方案:

>>> input_list = [('a', 1), ('a', 10), ('b', 244), ('c', 31) , ('c',45)]
>>> [[x for x in input_list if x[0]==a] for a in {x[0] for x in input_list if [k[0] for k in input].count(x[0])>1}]

将打印

>>> [[('a', 1), ('a', 10)], [('c', 31), ('c', 45)]]