Ограничение вхождений строк в списке на основе префикса

1

Поэтому код, над которым я работаю, предназначен для бота IRC, и я хочу реализовать способ ограничения каналов на основе CHANLIMIT сервера CHANLIMIT.

Опция CHANLIMIT представляет собой список ограничений с префиксом и пределом, CHANLIMIT : но если после : ничего нет, то ограничения нет.

Решение ниже работает, но я ищу какие-либо улучшения в нем.

result = ['#+:2', '&:']
channels = ['#test1', '#test2', '+test3', '&test4']

prefix_groups = [(prefix, []) for prefix in result]
channel_groups = {k: v for (k, v) in prefix_groups}
for channel in channels:
    for group in prefix_groups:
        if channel[0] in group[0]:
            channel_groups[group[0]].append(channel)
            break

for prefix, channels in channel_groups.items():
    limit = prefix.split(':')[1]
    if limit:
        if len(channels) > int(limit):
            channel_groups[prefix] = channels[:int(limit)]

channels = [
    channel for chanlist in channel_groups.values() for channel in chanlist]

print(channels)
  • 1
    Приведенное ниже решение должно сработать ... Вы можете уточнить, делает ли оно то, что вам нужно?
  • 0
    Только что сделал редактирование, решение работает, но я чувствую, что должен быть лучший способ сделать это.
Теги:
python-3.x
irc

5 ответов

0

Но давай бекап немного. Если я правильно понимаю, в конце вы хотите список элементов channels_to_test который соответствует префиксам и не превышает лимит префикса, если таковой имеется. Вы можете реализовать это поведение фильтрации в генераторе:

Решение 4

import re

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

def filter_channel_list(prefixes_to_match, input_channel_list):
    prefix_pattern = re.compile(r'^(.*):(\d+)?$')
    prefix_matches = (prefix_pattern.match(x) for x in prefixes_to_match)
    prefix_split = (x.groups() for x in prefix_matches)
    prefixes_remaining = {x: (int(y) if y is not None else None)
                          for (x, y) in prefix_split}

    for current_channel in input_channel_list:
        for (prefix, nb_left) in prefixes_remaining.items():
            if current_channel[0] in prefix:
                if nb_left is None:
                    yield current_channel
                    break
                else:
                    if nb_left > 0:
                        prefixes_remaining[prefix] -= 1
                        yield current_channel
                        break
                    else:
                        continue

result_channels = list(filter_channel_list(results, channels_to_test))

print(result_channels)

Вот несколько комментариев:

  • В этом решении я отменил требование, чтобы элемент channels_to_test соответствовал только одному элементу results. Это из-за операторов break размещенных в генераторе.
  • То, что мы делаем, - это наличие словаря с начальными ограничениями для каждого results и уменьшение значения каждый раз, когда мы сталкиваемся с совпадением с элементом channels_to_test. Если это значение станет 0, генератор перейдет к следующему значению. Это то, что делает (необязательно в данном случае) оператор continue.
  • 0
    Прежде всего, я хочу сказать, что вы сделали потрясающе со всеми этими ответами, больше, чем я ожидал от кого-либо. Теперь вы правы в предположении, что каналы будут соответствовать только одному из результатов, поскольку канал может иметь только один префикс, но при применении этого он всегда будет вызываться для создания списка, как и при использовании генератора. как это имеет какие-либо преимущества перед другими решениями, которые вы дали?
  • 0
    Спасибо! Я бы сказал, что, в общем, использование генератора позволяет отделить то, как вы выясняете, какие значения цикла for от того, что вы делаете с ними. В вашем конкретном случае одним из преимуществ может быть то, что вы итерируете только один раз для channels_to_test вместо того, чтобы создавать словарь, содержащий списки, которые вы должны в конечном итоге выровнять, чтобы извлечь свой ответ, но что более важно, вы выбираете решение, которое сможете чтобы понять через шесть месяцев, когда вы вернетесь к этому коду
0

Когда мне нужно извлечь некоторую информацию из строк, я склонен использовать регулярные выражения. Таким образом, расширяя Решение 2, мы можем получить:

Решение 3

import re
import itertools

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

prefix_pattern = re.compile(r'^(.*):(\d+)?$')
prefix_matches = (prefix_pattern.match(x) for x in results)
prefix_split = (x.groups() for x in prefix_matches)
channel_groups = {group: [channel for channel in channels_to_test
                                  if channel[0] in group[0]]
                  for group in prefix_split}

prefix_existing_limit = ((x, int(x[1])) for x in channel_groups
                         if x[1] is not None)
modified_channel_groups = {prefix_group: channel_groups[prefix_group][:limit]
                           for (prefix_group, limit) in prefix_existing_limit}

channel_groups.update(modified_channel_groups)

result_channels = list(itertools.chain.from_iterable(channel_groups.values()))

print(result_channels)
  • 0
    Я думаю, что решение 2 выглядит как очиститель многих решений, поэтому в этом случае регулярные выражения принесут большую пользу или это будет просто ваш стиль?
  • 0
    Ну, вы могли бы сказать, что это мой стиль, и в этом случае это может быть немного излишним. Вы только разбили строку на : чтобы получить вашу информацию. Но если у вас есть более точные требования для извлечения ваших групп, регулярные выражения могут оказаться полезными (например, в случае, если ваши результаты описаны по-другому). Регулярные выражения значительно упрощают поиск шаблонов в тексте. Но это зависит от вас, чтобы делать то, что вам подходит.
0

Есть много способов решить эту проблему. Делая некоторые минимальные упрощения, вы можете получить что-то вроде этого:

Решение 1

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

channel_groups = {k: [] for k in results}
for channel in channels_to_test:
    for group in results:
        if channel[0] in group:
            channel_groups[group].append(channel)
            break

for prefix, channels in channel_groups.items():
    limit = prefix.split(':')[1]
    if limit:
        limit = int(limit)
        channel_groups[prefix] = channels[:limit]

result_channels = [
    channel for chanlist in channel_groups.values() for channel in chanlist]

print(result_channels)

Вот изменения, которые я сделал:

  • Я создал channel_groups напрямую, вместо того, чтобы создавать список кортежей (prefix_groups), а затем использовал его для создания channel_groups
  • Я перебрал group по results вместо того, чтобы prefix_groups
  • Я не проверял, есть ли len(channels) > int(limit) потому что даже если длина channels меньше или равна limit, channels[:limit] вернет все channels
0

Мы можем пойти дальше:

Решение 2

import itertools

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

channel_groups = {group: [channel for channel in channels_to_test
                                  if channel[0] in group]
                  for group in results}

limit = lambda prefix: prefix.split(':')[1]

modified_channel_groups = {prefix: channels[:int(limit(prefix))]
                           for (prefix, channels) in channel_groups.items()
                           if limit(prefix)}

channel_groups.update(modified_channel_groups)

result_channels = list(itertools.chain.from_iterable(channel_groups.values()))

print(result_channels)

Но здесь я должен сделать предположение: я предположил, что канал может соответствовать максимум одному элементу results. Другими словами, никакие два элемента results будут соответствовать одному каналу. Скажите, если это не так для вашей ситуации.

Вот изменения, которые я сделал:

  • Я создал channel_groups используя словарное понимание, где каждое значение элемента - это понимание списка.
  • Я создал modified_channel_groups который содержит элементы channel_groups, которые были сокращены
  • Я обновил элементы channel_groups с теми же, что и modified_channel_groups
  • Я создал лямбда - modified_channel_groups выражения, чтобы я мог включить его в modified_channel_groups определения.
  • Я извлек результат result_channels используя itertools.chain.from_iterable()
  • 0
    Учитывая то, что вы сказали в другом комментарии: вы бы хотели принять этот ответ?
0

Вы можете даже дальше, так что вы создаете свой ответ channel_groups напрямую, но его становится труднее читать. Поэтому я не рекомендую это:

Решение 2а

import itertools

results = ['#+:2', '&:']
channels_to_test = ['#test1', '#test2', '+test3', '&test4',
                    '#test5', '!test5', '&test6', '&test7',
                    '+test8', '#test9']

limit = lambda prefix: prefix.split(':')[1]

channel_groups = {group: [channel for channel in channels_to_test if channel[0] in group][:int(limit(group)) if limit(group) else None]
                  for group in results}

result_channels = list(itertools.chain.from_iterable(channel_groups.values()))

print(result_channels)

Просто несколько вещей, чтобы отметить:

  • channel_groups создается так же, как в решении 2, но каждое значение словаря является списком (полученным из понимания), который разделен на целочисленное значение текущей group или None что будет означать принятие всех значений.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню