[英]What's an elegant way to extract a series of entries in a list of tuples into sublists?
Say I have a series of entries in a list of tuples like this:假设我在这样的元组列表中有一系列条目:
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)]
Now I want to extract a list with individual item entries like this:现在我想提取一个包含单个项目条目的列表,如下所示:
[(('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'))]
The delimiting keyword to identify a sublist is always item
or the end of the list.标识子列表的定界关键字始终是
item
或列表末尾。 There can be an arbitrary number of tuples between two adjacent delimiters.两个相邻的分隔符之间可以有任意数量的元组。 The content of the list before the first occurrence of
item
is to be ignored. item
第一次出现之前的列表内容将被忽略。 The detection of the keyword item
should be case insensitive.关键字
item
的检测应该不区分大小写。
I am not a Python afficionado, so I don't know how to apply something like list comprehension (if that's actually possible), given that I need to extract lists between delimiters.我不是 Python 爱好者,所以我不知道如何应用列表理解之类的东西(如果这真的可能的话),因为我需要在定界符之间提取列表。 Of course I can do it the pedestrian way by traversing through the list, identifying the positions of the keyword in the tuples and then extracting the sublists but I was hoping for a more elegant solution.
当然,我可以通过遍历列表、识别关键字在元组中的位置然后提取子列表来以行人方式完成,但我希望有一个更优雅的解决方案。
You can use itertools.groupby
for the task:您可以使用
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)
Prints:印刷:
[
(("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)),
]
You can use tuple()
function to transform lists into tuples, so you will be able to append all of the tuples inside listOfTuples
variable into the output that you need:您可以使用
tuple()
function 将列表转换为元组,这样您就可以将 append listOfTuples
变量中的所有元组转换为您需要的 output:
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:])
Output: Output:
[(('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)]]
The only disadvantage of this method is that you need to remove the first element of the output list of tuples, so it may doesn't work in certain situations.这种方法唯一的缺点是需要删除 output 元组列表的第一个元素,因此在某些情况下它可能不起作用。
OK, so in the meantime I learned more about list comprehension, the use of _
, *
and more than one index in a for loop.好的,所以与此同时,我了解了更多关于列表理解、
_
、 *
和 for 循环中多个索引的使用。 Besides, a good friend came up with the following solution:此外,一位好朋友提出了以下解决方案:
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
contains all occurrences of item
and the index of the last item in the list. Indices
包含所有出现的item
和列表中最后一项的索引。 Using indices
in a shifted fashion ( zip(indices[:-1], indices[1:])
) allows then the straightforward construction of out
.以移位的方式使用
indices
( zip(indices[:-1], indices[1:])
) 允许直接构建out
。
A constructive approach with no conditionals.一种没有条件的建设性方法。 Use
itertools.groupby
to group per 'item'
.使用
itertools.groupby
按'item'
分组。 Grouping is a True/False classification process ( ideally gives raise to an alternate sequence of True/False values! ), so you can slice by block of 2 and chain them together.分组是一个 True/False 分类过程(理想情况下会引发 True/False 值的交替序列! ),因此您可以按 2 个块进行切片并将它们链接在一起。
The split-right ( initial ) condition means that the 1st group with item
is found on the "right" and what is on the "left" can be forgotten ( [1:]
-part).右拆分( 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)
Same idea but with generators相同的想法,但有发电机
...
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)]
A ( logical different ) index based approach一种(逻辑上不同的)基于索引的方法
...
# 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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.