简体   繁体   English

将元组列表中的一系列条目提取到子列表中的优雅方法是什么?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM