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. 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. The detection of the keyword item
should be case insensitive.
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. 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.
CodePudding user response:
You can use itertools.groupby
for the task:
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)),
]
CodePudding user response:
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:
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:
[(('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.
CodePudding user response:
OK, so in the meantime I learned more about list comprehension, the use of _
, *
and more than one index in a for loop. 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. Using indices
in a shifted fashion (zip(indices[:-1], indices[1:])
) allows then the straightforward construction of out
.