根据前缀限制列表中字符串的出现
Limit occurences of strings in a list based on prefix
所以我正在处理的代码是针对 IRC 机器人的,我想实现一种基于 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
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
- 我在
results
上迭代了 group
而不是在 prefix_groups
上迭代
- 我没有查
len(channels) > int(limit)
是因为即使channels
的长度小于等于limit
,channels[:limit]
也会return 所有 channels
我们可以走得更远:
解决方案 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
元素
- 我 updated
channel_groups
的元素与 modified_channel_groups
的元素
- 我创建了一个 lambda expression 以便我可以将其包含在
modified_channel_groups
的定义中。
- 我使用
itertools.chain.from_iterable()
提取了result_channels
您甚至可以更进一步,直接创建您的答案 channel_groups
,但它变得更难阅读。所以不推荐:
解决方案 2a
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
这意味着采用所有值。
当我必须从字符串中提取一些信息时,我倾向于使用regular expressions。所以扩展解决方案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)
但是让我们后退一点。如果我理解正确,最后你想要一个 channels_to_test
的元素列表,它与前缀匹配并且不超过前缀的限制(如果有的话)。您可以在 generator:
中实现此过滤行为
解决方案 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
语句的作用。
所以我正在处理的代码是针对 IRC 机器人的,我想实现一种基于 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
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
- 我在
results
上迭代了group
而不是在prefix_groups
上迭代
- 我没有查
len(channels) > int(limit)
是因为即使channels
的长度小于等于limit
,channels[:limit]
也会return 所有channels
我们可以走得更远:
解决方案 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
元素 - 我 updated
channel_groups
的元素与modified_channel_groups
的元素
- 我创建了一个 lambda expression 以便我可以将其包含在
modified_channel_groups
的定义中。 - 我使用
itertools.chain.from_iterable()
提取了
result_channels
您甚至可以更进一步,直接创建您的答案 channel_groups
,但它变得更难阅读。所以不推荐:
解决方案 2a
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
这意味着采用所有值。
当我必须从字符串中提取一些信息时,我倾向于使用regular expressions。所以扩展解决方案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)
但是让我们后退一点。如果我理解正确,最后你想要一个 channels_to_test
的元素列表,它与前缀匹配并且不超过前缀的限制(如果有的话)。您可以在 generator:
解决方案 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
语句的作用。