Страницы

Поиск по вопросам

четверг, 2 января 2020 г.

Python: разбить список на список списков, по элементу-разделителю

#python #list #список #python_32


Python 3.2.
Есть список lst, в котором есть значения вперемешку с элементами-разделителями. Например,
["spam", "ham", None, "eggs", None, None, "bacon"]. Хочу получить список списков, разбив
lst по разделителю sep = None, т.е., получить [["spam", "ham"], ["eggs"], ["bacon"]].
Полистал стандартную библиотеку, но ничего похожего не нашел. На PyPi искать сложно,
быстрый пробег тоже ничего не дал. Наглая попытка проэксплуатировать str.split, разумеется,
провалилась с TypeError.
Посоветуйте, пожалуйста, более красивое решение, чем вот этот вырвиглазный монстр.
Не хочу ощущать себя Франкенштейном.
from functools import reduce

# Fugly.
def split_on(sep, lst):
    """
    Given an iterable `lst`, split it into iterable of lists by `sep`.

    >>> list(split_on(0, [1, 2, 3, 0, 4, 5, 0, 0, 6]))
    [[1, 2, 3], [4, 5], [6]]
    """
    s = sep if hasattr(sep, "__call__") else lambda x: x == sep
    return filter(lambda sublist: len(sublist) > 0,
                  reduce(lambda x, elem: x + [[]] if elem == sep
                                                else x[:-1] + [x[-1] + [elem]],
                         lst, [[]]))
    


Ответы

Ответ 1



Судя по всему, функциональное программирование оставило на вас серьезный отпечаток :) Сразу отмечу, что семантика split для случая вашего примера подразумевает возврат [["spam", "ham"], ["eggs"], [], ["bacon"]]. Это так, поскольку между None и None с точки зрения разделителей располагается пустой список. Так вот, решений можно придумать несколько. Наиболее explicit вариант подразумевает что-то в следующем духе: def split_on(what, delimiter = None): splitted = [[]] for item in what: if item == delimiter: splitted.append([]) else: splitted[-1].append(item) return splitted Понятно, что это решение работает с точностью до контракта функции касательно работы в случае пустого списка - [] и списка, состоящего только из разделителя - [None]. Я определил этот контракт следующим образом: [ ] -> [[ ]], [None] -> [[], []]. Для первого случая контракт довольно спорный. Во втором же случае результат получается, поскольку слева и справа от разделителя по сути расположены пустые последовательности. В случае, если вы захотите изменить это поведение, то модифицировать метод не должно составить особого труда. Пример использования: list1 = ["spam", "ham", None, "eggs", None, None, "bacon"] list2 = [] list3 = [None] list4 = ["eggs"] print split_on(list1) print split_on(list2) print split_on(list3) print split_on(list4) # Результат: [['spam', 'ham'], ['eggs'], [], ['bacon']] [[]] [[], []] [['eggs']] Из альтернативных вариантов - можно написать аналогичный предложенной функции генератор с yield'ами и, думаю, что можно придумать решение, разбивая предложенную итерабельную последовательность на группы, а дальше объединяя результаты путем groupby из itertools. Правда, мне кажется, что очевидность этих решений по сравнению с предложенным выше методом будет несколько хуже.

Ответ 2



m=["spam","ham","N","edg","N","N","back"] q=0 i=0 t=list(([],)*m.count("N")) for x in m: if x=="N": q=1 continue if q==1: i=i+1 t[i]=[] t[i].append(x) print(i,"---",t[i]) z=input() ============================= 0 --- ['spam'] 0 --- ['spam', 'ham'] 1 --- ['edg'] 2 --- ['back'] t=[['spam', 'ham'], ['edg'], ['back']]

Комментариев нет:

Отправить комментарий