Reputation: 75545
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 merge any element that ends in a \
with the element on its right, removing the \
in the process, and continue to do this until there are no more elements with a trailing \
.
The correct output looks like this.
['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 complex 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 more concise way to do this in Python, possibly using a more functional idiom?
I looked at reduce()
, but it seems to only let you move a reduce from a list to a single element, rather than to another list, but perhaps I am underestimating its power.
Upvotes: 3
Views: 464
Reputation: 123423
It may not be the shortest, but it's fairly 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)
Upvotes: 0
Reputation: 31339
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 it's cool and useful to understand (even if not to use)
Explanation: uses the aggregated list in reduce
and looks back to the last element to append to it if needed. Otherwise appends to list. Doesn't look back on first element to avoid exception.
['echo FooBar and some more foos', 'cat /etc/cpuinfo', 'cat /path/to/long/deep/directory/that/goes/on/forever']
Upvotes: 1
Reputation: 126
Not sure if more pythonic, but certainly more concise.
"\0".join(myList).replace("\\\0","").split("\0")
If you can't make assumptions that the strings don't include \0
you can generate the separator:
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)
Upvotes: 3
Reputation: 2239
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']
Upvotes: 2
Reputation: 114461
A possibly easier to read solution is
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:]
For me this is easier to read because the wp
"write-pointer" pattern to modify arrays inplace is something that I've in the fingers. Readiility is 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 really doesn't 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
Upvotes: 0