Concatenate elements from a list based on a criteria

Viewed 99

Let's say we have the list

L = ['a', '!b', '!c', 'd', 'e', '!f', 'g', 'h']

and we want to concatenate each element with the previous one if it starts with !.

Expected output:

M = ['a!b!c', 'd', 'e!f', 'g', 'h']

This works but it's probably unnecessarily complex to join the list into a string, and then to re-split the string into a list:

M = ''.join(('' if l.startswith('!') else '\n') + l for l in L).splitlines()[1:]
# ['a!b!c', 'd', 'e!f', 'g', 'h']

Is there a simpler way to concatenate elements from a list based on a criteria ('!' in l)?

3 Answers

Probably you are looking for a one-liner, but how about this not too complicated loop?

L = ['a', '!b', '!c', 'd', 'e', '!f', 'g', 'h']
M = []
for x in L:
    if x.startswith("!"):
        M[-1] += x
    else:
        M.append(x)
# ['a!b!c', 'd', 'e!f', 'g', 'h']

If you prefer short to readable, you could use a ternary using pop...

for x in L:
    M.append(M.pop() + x if x.startswith("!") else x)

I thought a bit more about a "clever" one-line solution with reduce, groupby, takewhile, or "join-and-split" solutions like the one you have, but if they work at all they just make it more complicated. I think this is one of those cases where if you want a one-liner, you should just define a function for it.

As a slightly more compact version of your join and split one-liner, if your elements are single characters (or something matchable with a well-defined regex), you can join into a string and then use re.findall to match the parts with ! in them:

import re

L = ['a', '!b', '!c', 'd', 'e', '!f', 'g', 'h']
print(re.findall('([a-z](?:![a-z])*)', ''.join(L)))

Output

['a!b!c', 'd', 'e!f', 'g', 'h']

If your list is actually comprised of elements more complex than single characters you would replace [a-z] in the regex (two places) with a regex that matched those elements.

You can join the elements, then remove the separators before '!' and split again:

L = ['a', '!b', '!c', 'd', 'e', '!f', 'g', 'h']

M = ",".join(L).replace(",!","!").split(",")

print(M)

# ['a!b!c', 'd', 'e!f', 'g', 'h']
Related