root
root

Reputation: 80456

Finding neighbors in a list

I have a list:

l=['a','>>','b','>>','d','e','f','g','>>','i','>>','>>','j','k','l','>>','>>']

I need to extract all the neighbors of '>>' and split them into groups where they have elements in between that are neither '>>' or neigbors of '>>'.

For the example list the expected outcome would be:

[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]

I have tried quite a few things, but all the simple ones have failed one way or another. At the moment the only code that seems to work is this:

def func(L,N):
    outer=[]
    inner=[]
    for i,e in enumerate(L):
        if e!=N:
            try:
                if L[i-1]==N or L[i+1]==N:
                    inner.append(e)
                elif len(inner)>0:
                    outer.append(inner)
                    inner=[] 
            except IndexError:
                pass
    if len(inner):
        outer.append(inner)
    return outer

func(l,'>>')

Out[196]:
[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]

Although it seems to work, i am wondering if there is a better,cleaner way to do it?

Upvotes: 4

Views: 2376

Answers (6)

Neil
Neil

Reputation: 489

Another medthod using superimposition of original list

import copy

lis_dup = copy.deepcopy(lis)

lis_dup.insert(0,'')
prev_in = 0
tmp=[]
res = []

for (x,y) in zip(lis,lis_dup):
    if '>>' in (x,y):
        if y!='>>' :
            if y not in tmp:
                tmp.append(y)
        elif x!='>>':
            if x not in tmp:
                print 'x is ' ,x
                tmp.append(x)
        else:
            if prev_in ==1:
                res.append(tmp)
                prev_in =0
                tmp = []
        prev_in  = 1
    else:
        if prev_in == 1:
            res.append(tmp)
            prev_in =0
            tmp = []
res.append(tmp)

print res

Upvotes: 0

Gareth Latty
Gareth Latty

Reputation: 89097

I would argue that the most pythonic and easy to read solution would be something like this:

import itertools

def neighbours(items, fill=None):
    """Yeild the elements with their neighbours as (before, element, after).

    neighbours([1, 2, 3]) --> (None, 1, 2), (1, 2, 3), (2, 3, None)

    """
    before = itertools.chain([fill], items)
    after = itertools.chain(items, [fill]) #You could use itertools.zip_longest() later instead.
    next(after)
    return zip(before, items, after)

def split_not_neighbour(seq, mark):
    """Split the sequence on each item where the item is not the mark, or next
    to the mark.

    split_not_neighbour([1, 0, 2, 3, 4, 5, 0], 0) --> (1, 2), (5)

    """
    output = []
    for items in neighbours(seq):
        if mark in items:
            _, item, _ = items
            if item != mark:
                output.append(item)
        else:
            if output:
                yield output
                output = []
    if output:
        yield output

Which we can use like so:

>>> l = ['a', '>>', 'b', '>>', 'd', 'e', 'f', 'g', '>>', 'i', '>>', '>>',
...      'j', 'k', 'l', '>>', '>>']
>>> print(list(split_not_neighbour(l, ">>")))
[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]

Note the neat avoidance of any direct indexing.

Edit: A more elegant version.

def split_not_neighbour(seq, mark):
    """Split the sequence on each item where the item is not the mark, or next
    to the mark.

    split_not_neighbour([1, 0, 2, 3, 4, 5, 0], 0) --> (1, 2), (5)

    """
    neighboured = neighbours(seq)
    for _, items in itertools.groupby(neighboured, key=lambda x: mark not in x):
        yield [item for _, item, _ in items if item != mark]

Upvotes: 2

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 251196

Something like this:

l=['a','>>','b','>>','d','e','f','g','>>','i','>>','>>','j','k','l','>>','>>']
l= filter(None,"".join(l).split(">>"))
lis=[]
for i,x in enumerate(l):
    if len(x)==1:
        if len(lis)!=0:
            lis[-1].append(x[0])
        else:
            lis.append([])
            lis[-1].append(x[0])
    else:
        if len(lis)!=0:
            lis[-1].append(x[0])
            lis.append([])
            lis[-1].append(x[-1])
        else:
            lis.append([])    
            lis[-1].append(x[0])
            lis.append([])
            lis[-1].append(x[-1])

print lis

output:

[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]

or:

l=['a','>>','b','>>','d','e','f','g','>>','i','>>','>>','j','k','l','>>','>>']
l= filter(None,"".join(l).split(">>"))
lis=[[] for _ in range(len([1 for x in l if len(x)>1])+1)]
for i,x in enumerate(l):
    if len(x)==1:
        for y in reversed(lis):
            if len(y)!=0:
                y.append(x)
                break
        else:
            lis[0].append(x)
    else:
        if not all(len(x)==0 for x in lis):
            for y in reversed(lis):
                if len(y)!=0:
                    y.append(x[0])
                    break
            for y in lis:
                if len(y)==0:
                    y.append(x[-1])
                    break    
        else:
            lis[0].append(x[0])
            lis[1].append(x[-1])

print lis

output:

[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]

Upvotes: 0

dm03514
dm03514

Reputation: 55972

my naive attempt

things = (''.join(l)).split('>>')

output = []
inner = []

for i in things:
    if not i:
        continue
    i_len = len(i)
    if i_len == 1:
        inner.append(i)
    elif i_len > 1:
        inner.append(i[0])
        output.append(inner)
        inner = [i[-1]]

output.append(inner)
print output # [['a', 'b', 'd'], ['g', 'i', 'j'], ['l']] 

Upvotes: 0

Anurag Uniyal
Anurag Uniyal

Reputation: 88865

You can simplify it like this

l = ['']+l+['']
stack = []
connected = last_connected = False
for i, item in enumerate(l):
    if item in ['','>>']: continue
    connected = l[i-1] == '>>' or  l[i+1] == '>>'
    if connected:
        if not last_connected:
            stack.append([])
        stack[-1].append(item)
    last_connected = connected

Upvotes: 2

Andrew Clark
Andrew Clark

Reputation: 208715

Here is one alternative:

import itertools

def func(L, N):
    def key(i_e):
        i, e = i_e
        return e == N or (i > 0 and L[i-1] == N) or (i < len(L) and L[i+1] == N)
    outer = []
    for k, g in itertools.groupby(enumerate(L), key):
        if k:
            outer.append([e for i, e in g if e != N])
    return outer

Or an equivalent version with a nested list comprehension:

def func(L, N):
    def key(i_e):
        i, e = i_e
        return e == N or (i > 0 and L[i-1] == N) or (i < len(L) and L[i+1] == N)
    return [[e for i, e in g if e != N] 
                for k, g in itertools.groupby(enumerate(L), key) if k]

Upvotes: 2

Related Questions