将元组列表中的一系列条目提取到子列表中的优雅方法是什么?
What's an elegant way to extract a series of entries in a list of tuples into sublists?
假设我在这样的元组列表中有一系列条目:
TRUE = 1
listOfTuples = [('selectable', 'frequency'), ('color', 'green'), ('item', '10 Hz'),
('value', 10), ('align', 'left'), ('hidden', TRUE), ('item', '20 Hz'),
('value', 20), ('align', 'right'), ('item', '50 Hz'), ('value', 50),
('item', '100 Hz'), ('value', 100), ('textColor', 0xFF0000)]
现在我想提取一个包含单个项目条目的列表,如下所示:
[(('item', '10 Hz'), ('value', 10), ('align', 'left'), ('hidden', TRUE)),
(('item', '20 Hz'), ('value', 20), ('align', 'right')),
(('item', '50 Hz'), ('value', 50)),
(('item', '100 Hz'), ('value', 100), ('textColor', '0xFF0000'))]
用于标识子列表的定界关键字始终是 item
或列表末尾。两个相邻的分隔符之间可以有任意数量的元组。 item
第一次出现之前的列表内容将被忽略。关键字item
的检测应该不区分大小写。
我不是 Python 爱好者,所以我不知道如何应用列表理解之类的东西(如果这真的可能的话),因为我需要在分隔符之间提取列表。当然,我可以通过遍历列表、识别关键字在元组中的位置然后提取子列表来以行人的方式完成,但我希望有一个更优雅的解决方案。
您可以使用 tuple()
函数将列表转换为元组,这样您就可以将 listOfTuples
变量中的所有元组附加到您需要的输出中:
TRUE = 1
lot = [('selectable', 'frequency'), ('color', 'green'), ('item', '10 Hz'),
('value', 10), ('align', 'left'), ('hidden', TRUE), ('item', '20 Hz'),
('value', 20), ('align', 'right'), ('item', '50 Hz'), ('value', 50),
('item', '100 Hz'), ('value', 100), ('textColor', 0xFF0000)]
l = [[]]
for i in lot:
if i[0]=='item':
l[-1] = tuple(l[-1])
l.append([])
l[-1].append(i)
print(l[1:])
输出:
[(('item', '10 Hz'), ('value', 10), ('align', 'left'), ('hidden', 1)), (('item', '20 Hz'), ('value', 20), ('align', 'right')), (('item', '50 Hz'), ('value', 50)), [('item', '100 Hz'), ('value', 100), ('textColor', 16711680)]]
此方法唯一的缺点是需要删除元组输出列表的第一个元素,因此在某些情况下可能不起作用。
您可以使用 itertools.groupby
完成任务:
from itertools import accumulate, groupby
TRUE = 1
listOfTuples = [
("selectable", "frequency"),
("color", "green"),
("item", "10 Hz"),
("value", 10),
("align", "left"),
("hidden", TRUE),
("item", "20 Hz"),
("value", 20),
("align", "right"),
("item", "50 Hz"),
("value", 50),
("item", "100 Hz"),
("value", 100),
("textColor", 0xFF0000),
]
a = accumulate(t == "item" for t, *_ in listOfTuples)
out = []
for _, g in groupby(zip(a, listOfTuples), lambda k: k[0]):
l = tuple(t for _, t in g)
if l[0][0] == "item":
out.append(l)
print(out)
打印:
[
(("item", "10 Hz"), ("value", 10), ("align", "left"), ("hidden", 1)),
(("item", "20 Hz"), ("value", 20), ("align", "right")),
(("item", "50 Hz"), ("value", 50)),
(("item", "100 Hz"), ("value", 100), ("textColor", 16711680)),
]
好的,所以与此同时我学习了更多关于列表理解、_
、*
的使用以及 for 循环中的多个索引。此外,一位好朋友提出了以下解决方案:
indices = [i for i, value in enumerate(listOfTuples)
if value[0].casefold() == "item"] + [len(listOfTuples)]
out = [listOfTuples[i:j] for i,j in zip(indices[:-1], indices[1:])]
Indices
包含所有出现的 item
和列表中最后一项的索引。以移位的方式使用 indices
(zip(indices[:-1], indices[1:])
) 可以直接构建 out
.
一种没有条件的建设性方法。使用 itertools.groupby
按 'item'
分组。分组是一个 True/False 分类过程( 理想情况下会引发 True/False 值的交替序列!),因此您可以按 2 块切片并将它们链接在一起.
split-right (initial) 条件意味着第一个 item
的组在“右边”找到,什么在“左边” " 可以忘记([1:]
-部分)。
import itertools as it
listOfTuples = # see question
# group by item & discard 1st group due to split-right condition
lst = tuple(tuple(grps) for _, grps in it.groupby(listOfTuples, lambda p: p[0] == 'item'))[1:]
# chain the slices
lst_new = [tuple(it.chain.from_iterable(lst[2*i:2*(i+1)])) for i in range(len(lst)//2)]
print(lst_new)
相同的想法,但使用了生成器
...
lst = (tuple(grps) for _, grps in it.groupby(listOfTuples, lambda p: p[0] == 'item'))
next(l) # split-right initial condition
l1, l2 = it.tee(lst)
n = len(tuple(l2))
lst_new = [tuple(it.chain.from_iterable(it.islice(l1, 0, 2, None))) for _ in range(n//2)]
一种(逻辑不同)基于索引的方法
...
# indices of the items
iter_ = filter(None, (tuple(grps)[0][0] if check else None for check, grps in it.groupby(enumerate(listOfTuples), lambda p: p[1][0] == 'item')))
# zip-stuffs
it1, it2 = it.tee(iter_)
next(it2)
it2 = it.chain(it2, iter((len(listOfTuples),)))
# apply the slices and cast to tuples
lst_new = list(map(tuple, map(listOfTuples.__getitem__, it.starmap(slice, zip(it1, it2)))))
print(lst_new)
假设我在这样的元组列表中有一系列条目:
TRUE = 1
listOfTuples = [('selectable', 'frequency'), ('color', 'green'), ('item', '10 Hz'),
('value', 10), ('align', 'left'), ('hidden', TRUE), ('item', '20 Hz'),
('value', 20), ('align', 'right'), ('item', '50 Hz'), ('value', 50),
('item', '100 Hz'), ('value', 100), ('textColor', 0xFF0000)]
现在我想提取一个包含单个项目条目的列表,如下所示:
[(('item', '10 Hz'), ('value', 10), ('align', 'left'), ('hidden', TRUE)),
(('item', '20 Hz'), ('value', 20), ('align', 'right')),
(('item', '50 Hz'), ('value', 50)),
(('item', '100 Hz'), ('value', 100), ('textColor', '0xFF0000'))]
用于标识子列表的定界关键字始终是 item
或列表末尾。两个相邻的分隔符之间可以有任意数量的元组。 item
第一次出现之前的列表内容将被忽略。关键字item
的检测应该不区分大小写。
我不是 Python 爱好者,所以我不知道如何应用列表理解之类的东西(如果这真的可能的话),因为我需要在分隔符之间提取列表。当然,我可以通过遍历列表、识别关键字在元组中的位置然后提取子列表来以行人的方式完成,但我希望有一个更优雅的解决方案。
您可以使用 tuple()
函数将列表转换为元组,这样您就可以将 listOfTuples
变量中的所有元组附加到您需要的输出中:
TRUE = 1
lot = [('selectable', 'frequency'), ('color', 'green'), ('item', '10 Hz'),
('value', 10), ('align', 'left'), ('hidden', TRUE), ('item', '20 Hz'),
('value', 20), ('align', 'right'), ('item', '50 Hz'), ('value', 50),
('item', '100 Hz'), ('value', 100), ('textColor', 0xFF0000)]
l = [[]]
for i in lot:
if i[0]=='item':
l[-1] = tuple(l[-1])
l.append([])
l[-1].append(i)
print(l[1:])
输出:
[(('item', '10 Hz'), ('value', 10), ('align', 'left'), ('hidden', 1)), (('item', '20 Hz'), ('value', 20), ('align', 'right')), (('item', '50 Hz'), ('value', 50)), [('item', '100 Hz'), ('value', 100), ('textColor', 16711680)]]
此方法唯一的缺点是需要删除元组输出列表的第一个元素,因此在某些情况下可能不起作用。
您可以使用 itertools.groupby
完成任务:
from itertools import accumulate, groupby
TRUE = 1
listOfTuples = [
("selectable", "frequency"),
("color", "green"),
("item", "10 Hz"),
("value", 10),
("align", "left"),
("hidden", TRUE),
("item", "20 Hz"),
("value", 20),
("align", "right"),
("item", "50 Hz"),
("value", 50),
("item", "100 Hz"),
("value", 100),
("textColor", 0xFF0000),
]
a = accumulate(t == "item" for t, *_ in listOfTuples)
out = []
for _, g in groupby(zip(a, listOfTuples), lambda k: k[0]):
l = tuple(t for _, t in g)
if l[0][0] == "item":
out.append(l)
print(out)
打印:
[
(("item", "10 Hz"), ("value", 10), ("align", "left"), ("hidden", 1)),
(("item", "20 Hz"), ("value", 20), ("align", "right")),
(("item", "50 Hz"), ("value", 50)),
(("item", "100 Hz"), ("value", 100), ("textColor", 16711680)),
]
好的,所以与此同时我学习了更多关于列表理解、_
、*
的使用以及 for 循环中的多个索引。此外,一位好朋友提出了以下解决方案:
indices = [i for i, value in enumerate(listOfTuples)
if value[0].casefold() == "item"] + [len(listOfTuples)]
out = [listOfTuples[i:j] for i,j in zip(indices[:-1], indices[1:])]
Indices
包含所有出现的 item
和列表中最后一项的索引。以移位的方式使用 indices
(zip(indices[:-1], indices[1:])
) 可以直接构建 out
.
一种没有条件的建设性方法。使用 itertools.groupby
按 'item'
分组。分组是一个 True/False 分类过程( 理想情况下会引发 True/False 值的交替序列!),因此您可以按 2 块切片并将它们链接在一起.
split-right (initial) 条件意味着第一个 item
的组在“右边”找到,什么在“左边” " 可以忘记([1:]
-部分)。
import itertools as it
listOfTuples = # see question
# group by item & discard 1st group due to split-right condition
lst = tuple(tuple(grps) for _, grps in it.groupby(listOfTuples, lambda p: p[0] == 'item'))[1:]
# chain the slices
lst_new = [tuple(it.chain.from_iterable(lst[2*i:2*(i+1)])) for i in range(len(lst)//2)]
print(lst_new)
相同的想法,但使用了生成器
...
lst = (tuple(grps) for _, grps in it.groupby(listOfTuples, lambda p: p[0] == 'item'))
next(l) # split-right initial condition
l1, l2 = it.tee(lst)
n = len(tuple(l2))
lst_new = [tuple(it.chain.from_iterable(it.islice(l1, 0, 2, None))) for _ in range(n//2)]
一种(逻辑不同)基于索引的方法
...
# indices of the items
iter_ = filter(None, (tuple(grps)[0][0] if check else None for check, grps in it.groupby(enumerate(listOfTuples), lambda p: p[1][0] == 'item')))
# zip-stuffs
it1, it2 = it.tee(iter_)
next(it2)
it2 = it.chain(it2, iter((len(listOfTuples),)))
# apply the slices and cast to tuples
lst_new = list(map(tuple, map(listOfTuples.__getitem__, it.starmap(slice, zip(it1, it2)))))
print(lst_new)