Создать новый список из другого списка на основе условия - PullRequest
0 голосов
/ 14 мая 2018

Я пытаюсь создать новый список из другого списка на основе условия:

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

Из какого состояния мне нужно снова фильтровать?

Я недостаточно опытен, чтобы использовать списки или встроенный фильтр, и я хотел бы оставить код максимально читабельным (для новичка), по крайней мере, до тех пор, пока я не узнаю больше.

UPDATE

Решение, предлагаемое 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) <--- должен быть последним </p>

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

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

  2. Числовое поле в кортеже не всегда равно 1, и иногда скрипт не учитывает его (отсутствует информация на моей стороне), потому что оно всегда принимает число, найденное в кортеже. Должен быть связан с кортежем Code и может быть опущен.

Я работаю над этим и обновлю свой пост, как только решу проблемы.

Ответы [ 3 ]

0 голосов
/ 14 мая 2018

Вы можете использовать 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
0 голосов
/ 18 мая 2018

Так как я не смог исправить проблемы, обнаруженные в решении, предложенном jpp (мой плохой, мне нужно уделить немного свободного времени, чтобы изучить больше), я разработал свой собственный код. Ясно, что это не «путь питона», но отлично работает:

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. После форматирования кортежей мне нужно было изменить список вывода (перевод строки), чтобы получить правильный порядок. Я позволил себе добавить несколько комментариев к лучшему чтению для таких новичков, как я.

Я знаю, что это грязное кодирование, и я вполне уверен, что могу добиться большего успеха, но сейчас у меня есть серьезные проблемы, связанные с комбинированием различных процедур понимания списка. Со временем я улучшу свои навыки кодирования.

0 голосов
/ 14 мая 2018

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...