Reputation: 5373
So recently, I wrote this piece of code. This gets a list of boolean values which comprises if all False
values interspersed with n
islands of True
values. Something like this:
[False, False, False, True, True, False, False, False, True, True, True, False]
# [island 1] [ island 2 ]
Now I want to separate these into n
lists where each of the islands are seaprated. For the above list, I would obtain:
[[False, False, False, True, True, False, False, False, False, False, False, False], #-> list has island 1
[False, False, False, False, False, False, False, False, True, True, True, False]] #-> list has island 2
This is the program I used to solve the problem:
import itertools as it
def getPosList(x):
segs = [ (s[0], list(s[1])) for s in it.groupby(x)]
allSegs = [ [] for s in segs if s[0] ]
i = 0
for s in segs:
s1, s2 = s
s2 = list(s2)
if s1:
for j, s in enumerate(allSegs):
if i == j: s += s2
else: s += len(s2)*[False]
i += 1
else:
for s in allSegs: s += s2
return allSegs
if __name__ == '__main__':
print getPosList([False, False, False, True, True, False, False, False, True, True, True, False])
This looks very unPythonic with loops and dummy variables. I feel there should be a more elegant solution than the one I just wrote. Something to do with dropwhile
, reduce
etc. However, I dont seem to be able to come up with something better. Any suggestions on improvement? I cringe every time I see this ugly piece of code.
Edit:
I took inspiration from feseje and came up with a recursive version.
import itertools as it
def ppListTF(xs):
return '[' + '|'.join([ '#' if x else ' ' for x in xs ]) + ']'
def getPosList1(x):
'''
'''
if x == []: return []
curX = list(it.takewhile(lambda m: m == x[0] , x))
nextVals = getPosList1(list(it.dropwhile(lambda m: m == x[0], x)))
curX1 = [False]*len(curX)
if nextVals:
curX2 = [False]*len(nextVals[0])
if x[0]:
# The first element is True. Split the list here
if nextVals: return [curX+curX2] + [ curX1+j for j in nextVals]
else: return [curX] + [curX1]
else:
# Just add the series of False to whatever is returned
if nextVals: return [ curX+j for j in nextVals]
else: return [curX]
if __name__ == '__main__':
inp = [False, False, False, True, True, False, False, False, True, True, True, False, True, True, False, False, False, False]
val = getPosList1(inp)
if any(inp):
val = filter( any, val )
print 'i', ppListTF(inp)
for i, v in enumerate(val):
print i, ppListTF(v)
I still like Hrabals answer. It is the best. Thanks to everyone who posted answers.
Upvotes: 0
Views: 1152
Reputation: 2525
Similar approach to Chris Martin's answer, but with no lambdas, just list comprehensions:
islands = [False, False, False, True, True, False, False, False, True, True, True, False]
islands_starts = [v[0]+1 for v in list(enumerate(zip(islands, islands[1:]))) if v[1] == (False, True)]
#[3, 8]
islands_ends = [v[0] for v in list(enumerate(zip(islands, islands[1:]))) if v[1] == (True, False)]
#[4, 10]
separated_islands = [[False if p not in range(i[0],i[1]+1) else True for p in range(0,len(islands))] for i in zip(islands_starts,islands_ends)]
#[[False, False, False, True, True, False, False, False, False, False, False, False],
#[False, False, False, False, False, False, False, False, True, True, True, False]]
Upvotes: 2
Reputation: 30736
I'm going to use 0 and 1 for brevity. This is Python 2.7. Not crazy about how this turned out, but maybe it'll give you some other ideas.
from itertools import *
a = map(int, list('1000111001001'))
start_indices = [i[0] for i in list(enumerate(zip(a, [0]+a))) if i[1] == (1, 0)]
# [0, 4, 9, 12]
get_island_length = lambda i: len(list(takewhile(lambda x: x, a[i:])))
pad = lambda b: b + [0]*(len(a)-len(b))
islands = [pad([0]*i + [1]*get_island_length(i)) for i in start_indices]
[''.join(map(str, i)) for i in islands]
# ['1000000000000', '0000111000000', '0000000001000', '0000000000001']
Upvotes: 2
Reputation: 4628
Although I agree with @grc that this is not really a suitable question here, I'd like to point out that you can do it with only one iteration. You go and process the elements one by one and create a new list for an island when you hit one.
def separateIslands(islandSegments):
separateIslands = []
empty = [False for x in islandSegments]
for i in range(0, len(islandSegments)-1):
if islandSegments[i]:
if i == 0 or not islandSegments[i-1]:
separateIslands.append(empty)
separateIslands[len(separateIslands) - 1][i] = True;
return separateIslands
islandSegments = [False, False, False, True, True, False, False, False, True, True, True, False]
print separateIslands(islandSegments)
This is probably not in any way more "pythonian" but still a simplification :)
Upvotes: 1