使用“itertools.groupby()”获取以“A”开头的字符串列表?
Using `itertools.groupby()` to get lists of runs of strings that start with `A`?
(抽象的)问题是这样的:我有一个日志文件
A: 1
A: 2
A: 3
B: 4
B: 5
A: 6
C: 7
D: 8
A: 9
A: 10
A: 11
我想以这样的列表结尾:
[["1", "2", "3"], ["6"], ["9", "10", "11"]]
其中文件已分解为 "runs" 个以 A
开头的字符串。我知道我可以使用 itertools.groupby
来解决这个问题,现在我有了这个解决方案(其中 f
是文件中行的列表)。
starts_with_a = lambda x: x.startswith("A")
coalesced = [g for _, g in groupby(f), key=starts_with_a]
runs = [re.sub(r'A: ', '', s) for s in coalesced if starts_with_a(s)]
所以我使用groupby,但是我必须过滤掉不以"A"开头的东西。这没关系,而且非常简洁,但是有没有更优雅的方法呢?我喜欢这样的方式:
- 不需要两次通过
- 更简洁 (and/or) 更具可读性
帮我驾驭 itertools
的力量!
是的,过滤掉不以A
开头的行,但使用groupby()
生成的key为每个组return编辑。它是 key
函数的 return 值,因此对于以 A
开头的那些行,它将是 True
。我会在这里使用 str.partition()
而不是正则表达式:
coalesce = (g for key, g in groupby(f, key=lambda x: x[:1] == "A") if key)
runs = [[res.partition(':')[-1].strip() for res in group] for group in coalesce]
由于您的 str.startswith()
参数是固定宽度的字符串文字,您不妨使用切片; x[:1]
切片第一个字符并将其与 'A'
进行比较,这为您提供与 x.startswith('A')
.
相同的测试
我使用生成器表达式对 groupby()
过滤进行分组;你 可以 将其内联到一个列表理解中:
runs = [[res.partition(':')[-1].strip() for res in group]
for key, group in groupby(f, key=lambda x: x[:1] == "A") if key]
演示:
>>> from itertools import groupby
>>> f = '''\
... A: 1
... A: 2
... A: 3
... B: 4
... B: 5
... A: 6
... C: 7
... D: 8
... A: 9
... A: 10
... A: 11
... '''.splitlines(True)
>>> coalesce = (g for key, g in groupby(f, key=lambda x: x[:1] == "A") if key)
>>> [[res.partition(':')[-1].strip() for res in group] for group in coalesce]
[['1', '2', '3'], ['6'], ['9', '10', '11']]
在没有 itertools 的一个循环中可能不是那么 pythonic 方式:
lines = '''
A: 1
A: 2
A: 3
B: 4
B: 5
A: 6
C: 7
D: 8
A: 9
A: 10
A: 11
'''
res = []
cont_last = []
for line in lines.splitlines():
if line.startswith('A: '):
cont_last.append(line.replace('A: ', ''))
else:
if cont_last:
res.append(cont_last)
cont_last = []
if cont_last:
res.append(cont_last)
print(res)
无需 itertools,只需一次迭代即可完成完整文件:
lines = open('logfile.txt','r').readlines()
out_list = []
temp_list = []
for line in lines:
if line.split(':')[0].strip() == 'A':
temp_list.append(line.split(':')[1].strip())
elif temp_list:
out_list.append(temp_list)
temp_list = []
if temp_list:
out_list.append(temp_list)
temp_list = []
print (out_list)
我知道你要求 itertools
我只是手头没有它,所以我无法调试它。希望这有帮助。
你想要简洁吗?好的,你知道了。
>>> lst = ['A: 1', 'A: 2', 'A: 3', 'B: 4', 'B: 5', 'A: 6', 'C: 7', 'D: 8', 'A: 9', 'A: 10', 'A: 11']
>>> [[x[1] for x in group[1]] for group in itertools.groupby((line.split(': ') for line in lst), key=lambda a:a[0]) if group[0]=='A']
[['1', '2', '3'], ['6'], ['9', '10', '11']]
从内到外分解:
(line.split(': ') for line in lst)
这是一个生成器表达式,它将每个元素拆分为其字母键和关联的字符串值。
for group in itertools.groupby(..., key=lambda a:a[0])
这只是根据在第一步中确定的字母键对元素进行分组。
... if group[0]=='A'
这只是排除了与问题中指定的条件不匹配的任何结果。如果字符串不是单个字符,您也可以使用 if not group[0].startswith('A')
。
[x[1] for x in group[1]] for ...]
这是一个列表推导式,它根据 groupby
的结果构建一个列表,该列表与之前的条件相匹配。 groupby
return 作为第二个 return 值 (group[1]
) 的迭代器,因此我们只需将该迭代器转换为具有列表理解的列表。 x[0]
是键值,x[1]
是后面的字符串。
[...]
所需的输出是一个列表,因此列表理解可以做到这一点。整个操作发生在一次输入上。
(抽象的)问题是这样的:我有一个日志文件
A: 1
A: 2
A: 3
B: 4
B: 5
A: 6
C: 7
D: 8
A: 9
A: 10
A: 11
我想以这样的列表结尾:
[["1", "2", "3"], ["6"], ["9", "10", "11"]]
其中文件已分解为 "runs" 个以 A
开头的字符串。我知道我可以使用 itertools.groupby
来解决这个问题,现在我有了这个解决方案(其中 f
是文件中行的列表)。
starts_with_a = lambda x: x.startswith("A")
coalesced = [g for _, g in groupby(f), key=starts_with_a]
runs = [re.sub(r'A: ', '', s) for s in coalesced if starts_with_a(s)]
所以我使用groupby,但是我必须过滤掉不以"A"开头的东西。这没关系,而且非常简洁,但是有没有更优雅的方法呢?我喜欢这样的方式:
- 不需要两次通过
- 更简洁 (and/or) 更具可读性
帮我驾驭 itertools
的力量!
是的,过滤掉不以A
开头的行,但使用groupby()
生成的key为每个组return编辑。它是 key
函数的 return 值,因此对于以 A
开头的那些行,它将是 True
。我会在这里使用 str.partition()
而不是正则表达式:
coalesce = (g for key, g in groupby(f, key=lambda x: x[:1] == "A") if key)
runs = [[res.partition(':')[-1].strip() for res in group] for group in coalesce]
由于您的 str.startswith()
参数是固定宽度的字符串文字,您不妨使用切片; x[:1]
切片第一个字符并将其与 'A'
进行比较,这为您提供与 x.startswith('A')
.
我使用生成器表达式对 groupby()
过滤进行分组;你 可以 将其内联到一个列表理解中:
runs = [[res.partition(':')[-1].strip() for res in group]
for key, group in groupby(f, key=lambda x: x[:1] == "A") if key]
演示:
>>> from itertools import groupby
>>> f = '''\
... A: 1
... A: 2
... A: 3
... B: 4
... B: 5
... A: 6
... C: 7
... D: 8
... A: 9
... A: 10
... A: 11
... '''.splitlines(True)
>>> coalesce = (g for key, g in groupby(f, key=lambda x: x[:1] == "A") if key)
>>> [[res.partition(':')[-1].strip() for res in group] for group in coalesce]
[['1', '2', '3'], ['6'], ['9', '10', '11']]
在没有 itertools 的一个循环中可能不是那么 pythonic 方式:
lines = '''
A: 1
A: 2
A: 3
B: 4
B: 5
A: 6
C: 7
D: 8
A: 9
A: 10
A: 11
'''
res = []
cont_last = []
for line in lines.splitlines():
if line.startswith('A: '):
cont_last.append(line.replace('A: ', ''))
else:
if cont_last:
res.append(cont_last)
cont_last = []
if cont_last:
res.append(cont_last)
print(res)
无需 itertools,只需一次迭代即可完成完整文件:
lines = open('logfile.txt','r').readlines()
out_list = []
temp_list = []
for line in lines:
if line.split(':')[0].strip() == 'A':
temp_list.append(line.split(':')[1].strip())
elif temp_list:
out_list.append(temp_list)
temp_list = []
if temp_list:
out_list.append(temp_list)
temp_list = []
print (out_list)
我知道你要求 itertools
我只是手头没有它,所以我无法调试它。希望这有帮助。
你想要简洁吗?好的,你知道了。
>>> lst = ['A: 1', 'A: 2', 'A: 3', 'B: 4', 'B: 5', 'A: 6', 'C: 7', 'D: 8', 'A: 9', 'A: 10', 'A: 11']
>>> [[x[1] for x in group[1]] for group in itertools.groupby((line.split(': ') for line in lst), key=lambda a:a[0]) if group[0]=='A']
[['1', '2', '3'], ['6'], ['9', '10', '11']]
从内到外分解:
(line.split(': ') for line in lst)
这是一个生成器表达式,它将每个元素拆分为其字母键和关联的字符串值。
for group in itertools.groupby(..., key=lambda a:a[0])
这只是根据在第一步中确定的字母键对元素进行分组。
... if group[0]=='A'
这只是排除了与问题中指定的条件不匹配的任何结果。如果字符串不是单个字符,您也可以使用 if not group[0].startswith('A')
。
[x[1] for x in group[1]] for ...]
这是一个列表推导式,它根据 groupby
的结果构建一个列表,该列表与之前的条件相匹配。 groupby
return 作为第二个 return 值 (group[1]
) 的迭代器,因此我们只需将该迭代器转换为具有列表理解的列表。 x[0]
是键值,x[1]
是后面的字符串。
[...]
所需的输出是一个列表,因此列表理解可以做到这一点。整个操作发生在一次输入上。