Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Insert element to list based on previous and next elements

Tags:

python

list

I'm trying to add a new tuple to a list of tuples (sorted by first element in tuple), where the new tuple contains elements from both the previous and the next element in the list.

Example:

oldList = [(3, 10), (4, 7), (5,5)]
newList = [(3, 10), (4, 10), (4, 7), (5, 7), (5, 5)]

(4,10) was constructed from and added in between (3,10) and (4,7).

Construct (x,y) from (a,y) and (x,b)

I've tried using enumerate() to insert at the specific position, but that doesn't really let me access the next element.

like image 796
Silviu Tofan Avatar asked Jul 09 '16 19:07

Silviu Tofan


3 Answers

oldList = [(3, 10), (4, 7), (5,5)]

def pair(lst):
    # create two iterators
    it1, it2 = iter(lst), iter(lst)
    # move second to the second tuple
    next(it2)
    for ele in it1:
        # yield original
        yield ele
        # yield first ele from next and first from current
        yield (next(it2)[0], ele[1])

Which will give you:

In [3]: oldList = [(3, 10), (4, 7), (5, 5)]

In [4]: list(pair(oldList))
Out[4]: [(3, 10), (4, 10), (4, 7), (5, 7), (5, 5)]

Obviously we need to do some error handling to handle different possible situations.

You could also do it using a single iterator if you prefer:

def pair(lst):
    it = iter(lst)
    prev = next(it)
    for ele in it:
        yield prev
        yield (prev[0], ele[1])
        prev = ele
    yield (prev[0], ele[1])

You can use itertools.tee in place of calling iter:

from itertools import tee
def pair(lst):
    # create two iterators
    it1, it2 = tee(lst)
    # move second to the second tuple
    next(it2)
    for ele in it1:
        # yield original
        yield ele
        # yield first ele from next and first from current
        yield (next(it2)[0], ele[1])
like image 59
Padraic Cunningham Avatar answered Nov 14 '22 19:11

Padraic Cunningham


You can use a list comprehension and itertools.chain():

>>> list(chain.from_iterable([((i, j), (x, j)) for (i, j), (x, y) in zip(oldList, oldList[1:])])) + oldList[-1:]
[(3, 10), (4, 10), (4, 7), (5, 7), (5, 5)]
like image 29
Mazdak Avatar answered Nov 14 '22 19:11

Mazdak


Not being a big fan of one-liners (or complexity) myself, I will propose a very explicit and readable (which is usually a good thing!) solution to your problem.

So, in a very simplistic approach, you could do this:

def insertElements(oldList):
    """
    Return a new list, alternating oldList tuples with 
    new tuples in the form (oldList[i+1][0],oldList[i][1])
    """
    newList = []
    for i in range(len(oldList)-1):
      # take one tuple as is
      newList.append(oldList[i])
      # then add a new one with items from current and next tuple
      newList.append((oldList[i+1][0],oldList[i][1]))
    else:
      # don't forget the last tuple
      newList.append(oldList[-1])
    return newList

oldList = [(3, 10), (4, 7), (5, 5)]
newList = insertElements(oldList)

That will give you the desired result in newList:

print(newList)
[(3, 10), (4, 10), (4, 7), (5, 7), (5, 5)]

This is not much longer code than other more sophisticated (and memory efficient!) solutions, like using generators, AND I consider it a lot easier to read than intricate one-liners. Also, it would be easy to add some checks to this simple function (like making sure you have a list of tuples).

Unless you already know you need to optimize this particular piece of your code (assuming this is part of a bigger project), this should be good enough. At the same time it is: easy to implement, easy to read, easy to explain, easy to maintain, easy to extend, easy to refactor, etc.

Note: all other previous answers to your question are also better solutions than this simple one, in many ways. Just wanted to give you another choice. Hope this helps.

like image 34
Daniele Barresi Avatar answered Nov 14 '22 21:11

Daniele Barresi