Как получить каждое значение после определенного символа в списке/массиве

#python #arrays #list #algorithm #arraylist

Вопрос:

Например, если у меня есть список

 data = ['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I']
 

Как я могу получить каждый индекс после B (включая B), пока он не встретится с другим B или O?
Например

 output => [[2,3,4],[5,6],[8,9]]
 

Потому что первый B, I, I находится в индексе 2, 3, 4
, второй-B, I, который находится в индексе 5 и 6
, и последний-B, I, который находится в индексе 8 и 9

Еще один пример

 data = ['B', 'I', 'I', 'O', 'O', 'B', 'I', 'B', 'I', 'I', 'O']
output => [[0, 1, 2], [5, 6], [7, 8, 9]]
 

Я думаю повторить список и проверить один за другим. Но есть ли какой-нибудь более чистый и эффективный способ сделать это? Спасибо

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

1. Неясно, что вы пытаетесь сделать, пожалуйста, уточните.

2. Но индекс элементов идет после того, как B есть [3,6,9] . не [[2,3,4],[5,6],[8,9]]

3. извините, я имею в виду каждый индекс после B (включая B), пока он не встретится с другим B или O

4. @MuhammadFhadli цель переполнения стека-решить возникающие у вас проблемы и расширить понимание людьми программирования, чтобы оно не работало на вас, если вы хотите получить ответ на конкретную проблему, пожалуйста, включите это в свой вопрос.

Ответ №1:

Попробуй это!

 data = ['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I']

res = []

i = -1
is_first = False
o_found = False
for index, value in enumerate(data):
    if value == 'O':
        o_found=True
    if value != 'B' and o_found:
        continue
    if value == 'B':
        i =1
        is_first = True
        o_found=False
        res.append([])
    if is_first and o_found is False:
        res[i].append(index)

print(res)
 

Ответ №2:

Вы можете найти индексы 'B' и срезать список по ним. Каждый подсписок может быть срезан по индексу 'O' и индексам, возвращаемым простым циклом

 def sub_list(lst, index):
    lst = lst[:lst.index('O') if 'O' in lst else len(lst)]
    # or with itertools:
    # lst = list(itertools.takewhile(lambda x: x != 'O', lst))
    return [i   index for i in range(len(lst))]


def get_indices(data):
    indices = [i for i, x in enumerate(data) if x == 'B']   [len(data)]
    return [sub_list(data[indices[i]:indices[i   1]], indices[i]) for i in range(len(indices) - 1)]


data1 = ['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I']
data2 = ['B', 'I', 'I', 'O', 'O', 'B', 'I', 'B', 'I', 'I', 'O']
print(get_indices(data1)) # [[2, 3, 4], [5, 6], [8, 9]]
print(get_indices(data2)) # [[0, 1, 2], [5, 6], [7, 8, 9]]
 

Ответ №3:

Обычно я бы рекомендовал попытаться разработать решение, используя функции группировки из таких модулей, как itertools или more_itertools . Однако, поскольку у вас есть два разных символа-разделителя, » B » и «O», которые ведут себя по-разному, и поскольку вы хотите создавать списки индексов, таких как [[2,3,4],[5,6],[8,9]] , а не группы значений, например ['BII', 'BI', 'BI'] , это будет немного громоздко. Вам придется использовать enumerate , чтобы получить индексы вместе со значениями, разделить их в зависимости от значений, выполнить дополнительную работу по различению B и O, затем отбросить значения и сохранить только индексы.

С помощью more_itertools.split_before

Модуль more_itertools имеет несколько функций, которые очень хороши для группировки/нарезки/разделения/создания окон, таких как more_itertools.split_before :

 from more_itertools import split_before

print(list(split_before('OOBIIBIOBI', lambda c: c in 'BO')))
[['O'], ['O'], ['B', 'I', 'I'], ['B', 'I'], ['O'], ['B', 'I']]

def split_B_O(seq):
    yield from (next(zip(*l)) for l in split_before(enumerate(seq), lambda p: p[1] in 'BO') if l[0][1] == 'B')

print(list(split_B_O('OOBIIBIOBI')))
# [(2, 3, 4), (5, 6), (8, 9)]

print(list(split_B_O('BIIOOBIBIIO')))
# [(0, 1, 2), (5, 6), (7, 8, 9)]

print(list(split_B_O('OBI-WAN KENOBI')))
# [(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), (12, 13)]

 

С помощью itertools.groupby

Группировка смежных значений является целью функции itertools.groupby . Он группирует значения в соответствии с «ключом», который является функцией, передаваемой в качестве параметра. Здесь мы напишем ключ, который возвращает другой идентификатор каждый раз, когда он встречается с » O » или «B», и возвращает тот же идентификатор, что и ранее, если он встречается с другим символом.

 from itertools import groupby

def k(c):
    if c[1] in 'OB':
        k.idx  = 1
    return k.idx

k.idx = 0

def split_B_O(seq):
    k.idx = 0
    for _,g in groupby(enumerate(seq), k):
        g = list(g)
        if g[0][1] == 'B':
            yield next(zip(*g))

print(list(split_B_O('OOBIIBIOBI')))
# [(2, 3, 4), (5, 6), (8, 9)]

print(list(split_B_O('BIIOOBIBIIO')))
# [(0, 1, 2), (5, 6), (7, 8, 9)]

print(list(split_B_O('OBI-WAN KENOBI')))
# [(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), (12, 13)]
 

Написание собственного генератора

Однако, поскольку 'B' и 'O' играют разную роль в разделении, а большинство функций разделения, которые можно найти в универсальном модуле python, не могут учитывать две разные роли разделяющего символа, я думаю, что проще написать функцию самостоятельно с помощью цикла for и некоторых переменных.

 def split_B_O(seq):
    inside_group = False
    j = 0
    for i,c in enumerate(seq):
        if c == 'O' and inside_group:
            yield range(j,i)
            inside_group = False
        elif c == 'B' and inside_group:
            yield range(j, i)
            j = i
        elif c == 'B':
            j = i
            inside_group = True
    if inside_group:
        yield range(j, i 1)

print(list(split_B_O(['O', 'O', 'B', 'I', 'I', 'B', 'I', 'O', 'B', 'I'])))
# [range(2, 5), range(5, 7), range(8, 10)]

print(list(split_B_O(['B', 'I', 'I', 'O', 'O', 'B', 'I', 'B', 'I', 'I', 'O'])))
# [range(0, 3), range(5, 7), range(7, 10)]

print([list(r) for r in split_B_O('OBI-WAN KENOBI')])
# [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [12, 13]]
 

Если по какой-то причине вам абсолютно не нравятся range объекты, вы можете yield range(j,i 1) заменить yield list(range(j,i 1)) их на получение списков индексов. Но range объекты-это здорово, поэтому я не рекомендую этого делать.