Home > Mobile >  What's an elegant way to extract a series of entries in a list of tuples into sublists?
What's an elegant way to extract a series of entries in a list of tuples into sublists?

Time:05-01

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.

  • Related