Is there a more Pythonic way to conditionally concatenate adjacent list items?

Suppose I have the following list.

myList = ["echo FooBar \\", "and some more foos", "cat /etc/cpuinfo", 
    "cat /path/to/\\", "long/deep/directory/\\", "that/goes/on/forever"]

      

I want to concatenate any element that ends in \

with the element on the right, deleting \

in the process and continuing to do so until there are no more elements ending in \

.

The correct output is as follows.

['echo FooBar and some more foos', 
 'cat /etc/cpuinfo', 
 'cat /path/to/long/deep/directory/that/goes/on/forever']

      

Here is my current solution, which is functional but seems much more complicated than necessary.

myList = ["echo FooBar \\", "and some more foos", "cat /etc/cpuinfo", 
    "cat /path/to/\\", "long/deep/directory/\\", "that/goes/on/forever"]

tmpItem = None
tmpList = []

for x in myList:
   if tmpItem:
     if tmpItem.endswith("\\"):
        tmpItem = tmpItem[:-1] + x
     else:
        tmpList.append(tmpItem)
        tmpItem = x
   else: tmpItem = x

if tmpItem:
     if tmpItem.endswith("\\"):
        tmpList.append(tmpItem[:-1])
     else:
        tmpList.append(tmpItem)

print tmpList

      

Is there a shorter way to do this in Python, perhaps using a more functional idiom?

I've looked reduce()

, but it looks like you can move the abbreviation from a list to one item rather than another list, but perhaps I am underestimating its power.

+3


source to share


5 answers


Not sure if more pythonic, but more concise of course.

"\0".join(myList).replace("\\\0","").split("\0")

      



If you can't make the assumption that the lines don't include \0

, you can generate a delimiter:

import string
import random
sep = ""
while any(sep in s for s in myList):
    sep += random.choice(string.ascii_uppercase + string.digits)
sep.join(myList).replace("\\"+sep,"").split(sep)

      

+3


source


myList = ["echo FooBar \\", "and some more foos", "cat /etc/cpuinfo", 
    "cat /path/to/\\", "long/deep/directory/\\", "that/goes/on/forever"]
ret = []
for i in myList:
    if ret and ret[-1].endswith('\\'):
        ret[-1] = ret[-1][:-1] + i
    else:
        ret.append(i)

print ret

      

prints



['echo FooBar and some more foos', 'cat /etc/cpuinfo',
 'cat /path/to/long/deep/directory/that/goes/on/forever']

      

+2


source


If this is pythonic for you:

reduce(lambda agg, x: (agg +
                        [agg.pop()[:-1] + x] if agg[-1].endswith('\\') else
                         agg + [x]) if len(agg) > 0 else
                         agg + [x], myList, [])

      

I think this is cool and useful to understand (even if not used)

Explanation: Uses the aggregated list in reduce

and refers to the last item to add to it if needed. Otherwise, it is added to the list. Doesn't look back at the first element to avoid an exception.

['echo FooBar and some more foos', 'cat / etc / cpuinfo', 'cat / path / to / long / deep / directory / that / go / on / forever']

+1


source


It might be easier to read the solution

wp = 0
for L in myList:
    if wp and myList[wp - 1].endswith('\\'):
        myList[wp - 1] = myList[wp - 1][:-1] + L
    else:
        myList[wp] = L
        wp += 1
del myList[wp:]

      

It's easier to read for me because the wp

"write-pointer" pattern for modifying arrays in place is what I have in my fingers. Readability in the eye of the beholder ...

a purely functional solution (no assignments at all) could be

def merge_continuations(L, ix=0):
    if ix >= len(L)-1:
        return L
    elif L[ix].endswith('\\'):
        return merge_continuations(L[:ix] +
                                   [L[ix][:-1] + L[ix+1]] +
                                   L[ix+2:], ix)
    else:
        return merge_continuations(L, ix+1)

      

but it doesn't really belong to Python.

Another version could be written as a generator, accepting any iterable:

def merge_continuations(it):
    prefix = None
    for L in it:
        if prefix is None:
            prefix = L
        elif prefix.endswith('\\'):
            prefix = prefix[:-1] + L
        else:
            yield prefix
            prefix = L
    if prefix is not None:
        yield prefix

      

not my preferred approach, but idiomatic in Python

0


source


It may not be the shortest, but quite readable and Pythonic.

out, grp = [], []
for s in my_list:
    if s.endswith('\\'):
        grp.append(s[:-1])
    else:
        out.append(''.join(grp) + s)
        grp = []

print(out)

      

0


source







All Articles