根据条件从另一个列表创建新列表

时间:2018-05-14 18:47:02

标签: python python-2.7 list filter conditional-statements

我正在尝试根据条件从另一个列表创建一个新列表:

lst = [("Id01","Code1",1),("Id01","#instr1",1),("Id01","#instr2",1),("Id01","#instr4",1),
       ("Id01","Code2",1),("Id01","#instr3",1),("Id01","#instr2",1),("Id02","Code2",1),
       ("Id02","#instr2",1),("Id02","#instr5",1)]

table, instrlist = '', ''; code, instructions = [], []; qty = 0

for idx, l in enumerate(lst):
    table = l[0]
    if not l[1].startswith('#'):
        code = l[1]; qty = l[2]; instructions = []
    else:
        instructions.append(l[1])
    print idx, table, code, instructions, qty

每次代码在包含'#'的元组之后出现在元组中时,我需要将正确的行传输到程序的另一部分并重置以开始处理另一部分。我提出了一系列条件,我得到了这个结果:

0 Id01 Code1 [] 1
1 Id01 Code1 ['#instr1'] 1
2 Id01 Code1 ['#instr1', '#instr2'] 1
3 Id01 Code1 ['#instr1', '#instr2', '#instr4'] 1
4 Id01 Code2 [] 1
5 Id01 Code2 ['#instr3'] 1
6 Id01 Code2 ['#instr3', '#instr2'] 1
7 Id02 Code2 [] 1
8 Id02 Code2 ['#instr2'] 1
9 Id02 Code2 ['#instr2', '#instr5'] 1

然而我真正需要的结果是

3 Id01 Code1 ['#instr1', '#instr2', '#instr4'] 1
6 Id01 Code2 ['#instr3', '#instr2'] 1
9 Id02 Code2 ['#instr2', '#instr5'] 1

我需要再次过滤什么条件?

我不够熟练使用列表理解或内置过滤器,我想尽可能多地保留代码(对于新手),至少在我了解更多之前。

更新

jpp 提供的解决方案似乎是最有效和最易读的:

from collections import defaultdict
from itertools import count, chain

lst = [("Id01","Code1",1),("Id01","#instr1",1),("Id01","#instr2",1),("Id01","#instr4",1),
       ("Id01","Code2",1),("Id01","#instr3",1),("Id01","#instr2",1),("Id02","Code2",1),
       ("Id02","#instr2",1),("Id02","#instr5",1)]

d = defaultdict(list)
enums = []
c = count()

for ids, action, num in lst:
    if not action.startswith('#'):
        my_ids, my_action = ids, action
        enums.append(next(c))
    else:
        d[(my_ids, my_action)].append([action, num])
        next(c)
enums = enums[1:] + [len(lst)]

for idx, ((key1, key2), val) in enumerate(d.items()):
    print (enums[idx]-1, key1, key2, list(chain.from_iterable(val)), val[0][-1])

但是我面临一些问题。

  1. 由于某些原因,订单错误(最后一行成为第一行): 结果:

    (3,'Id02','Code2',['#instr2',1,'#instr5',1],1)< ---应该是最后一个

    (6,'Id01','Code1',['#instr1',1,'#instr2',1,'#instr4',1],1)

    (9,'Id01','Code2',['#instr3',1,'#instr2',1],1)

  2. 元组上的数字字段并不总是“1”,有时脚本不会尊重它(我身边缺少信息),因为它总是需要在元组中找到的数字。需要与'Code'元组配对,可以省略。

  3. 我正在努力,我会在解决问题后立即更新我的帖子。

3 个答案:

答案 0 :(得分:1)

collections.defaultdict提供直观的解决方案。我们的想法是创建一个字典,如果第二个元素不以'#'开头,则将键设置为元组的前两个组件。然后以您想要的格式将字典迭代到print

itertools.count有一些杂乱无章的工作来获得你想要的指数。我相信你可以改进这项工作。

from collections import defaultdict
from itertools import count, chain

lst = [("Id01","Code1",1),("Id01","#instr1",1),("Id01","#instr2",1),("Id01","#instr4",1),
       ("Id01","Code2",1),("Id01","#instr3",1),("Id01","#instr2",1),("Id02","Code2",1),
       ("Id02","#instr2",1),("Id02","#instr5",1)]

d = defaultdict(list)
enums = []
c = count()

for ids, action, num in lst:
    if not action.startswith('#'):
        my_ids, my_action = ids, action
        enums.append(next(c))
    else:
        d[(my_ids, my_action)].append([action, num])
        next(c)

enums = enums[1:] + [len(lst)]

结果:

for idx, ((key1, key2), val) in enumerate(d.items()):
    print(enums[idx]-1, key1, key2, list(chain.from_iterable(val)), val[0][-1])

3 Id01 Code1 ['#instr1', 1, '#instr2', 1, '#instr4', 1] 1
6 Id01 Code2 ['#instr3', 1, '#instr2', 1] 1
9 Id02 Code2 ['#instr2', 1, '#instr5', 1] 1

答案 1 :(得分:1)

您可以使用itertools.groupby

import itertools 
import re
lst = [("Id01","Code1",1),("Id01","#instr1",1),("Id01","#instr2",1),("Id01","#instr4",1),
   ("Id01","Code2",1),("Id01","#instr3",1),("Id01","#instr2",1),("Id02","Code2",1),
   ("Id02","#instr2",1),("Id02","#instr5",1)]
results = {a:list(b) for a, b in itertools.groupby(sorted(lst, key=lambda x:x[0]), key=lambda x:x[0])}
code_groupings = {a:[[c, list(d)] for c, d in itertools.groupby(b, key=lambda x:'Code' in x[1])] for a, b in results.items()}
count = 0
last_code = None
for a, b in sorted(code_groupings.items(), key=lambda x:x[0]):
  for c, results in b:
    if c:
      count += 3
      last_code = results[0][1]
    else:
      print('{} {} {} {} 1'.format(count, a, last_code, str([i[1] for i in results])))

输出:

3 Id01 Code1 ['#instr1', '#instr2', '#instr4'] 1
6 Id01 Code2 ['#instr3', '#instr2'] 1
9 Id02 Code2 ['#instr2', '#instr5'] 1

答案 2 :(得分:0)

由于我无法纠正我在jpp提供的解决方案中发现的问题(我的不好,我需要花些时间学习更多),我已经详细阐述了自己的代码。显然不是" python方式",但工作正常:

lst = [("Id01","Code1",1),("Id01","#instr1",1),("Id01","#instr2",1),("Id01","#instr4",1),
       ("Id01","Code2",1),("Id01","#instr3",1),("Id01","#instr2",1),("Id02","Code2",1),
       ("Id02","#instr2",1),("Id02","#instr5",1)]

instr, newline = [], []
for idx, codex, qtx in reversed(lst): #reversed list is more simple to read

    if codex.startswith('#'):
        instr.insert(0, codex) #here I'm creating the tuple in the right order
    else:
        newline += tuple([(idx, codex, qtx) + tuple(instr)])
        instr = []

newline = newline[::-1] #reversed the list to respect the order of the original list (lst) 

for n in newline:
    print n

结果:

('Id01', 'Code1', 1, '#instr1', '#instr2', '#instr4')
('Id01', 'Code2', 1, '#instr3', '#instr2')
('Id02', 'Code2', 1, '#instr2', '#instr5')

基本思想是恢复输入列表(lst),因为在for循环上更简单地阐述条件。格式化元组后,我需要反转输出列表(换行符)以获得正确的顺序。 我冒昧地为像我这样的新手更好地阅读了一些评论。

我知道这是一个肮脏的编码,我确信我可以做得更好,但是现在我遇到了严重的问题,结合了各种列表理解程序。 我会及时提高编码技能。