Michał Trybus
Michał Trybus

Reputation: 11784

Insert an element between two adjacent list elements which meet a condition

In python, what's a clean way to insert an element between any two elements that meet a condition?

A call like:

insert_between([1,2,3,4,7,8,9,15,16], 0, lambda x,y: x + 1 != y)

should produce:

[1,2,3,4,0,7,8,9,0,15,16]

Is there a better way than to iterate and append to second list?

Upvotes: 3

Views: 4772

Answers (3)

Prem Anand
Prem Anand

Reputation: 2537

Can be done easily using lambda function and reduce

l=[1, 2, 3, 4, 7, 8, 9, 15, 16]
f = lambda l, i: l+[0,i] if l and l[-1]+1!=i else l+[i]
print reduce(f, l, [])
[1, 2, 3, 4, 0, 7, 8, 9, 0, 15, 16]

Upvotes: 2

kindall
kindall

Reputation: 184101

@katrielalex's version is probably the most efficient way to do it, both in terms of time and memory. Here is a similar version that returns a new list rather than an iterator.

def insert_between(items, insert_item, compare):
    result = items[:1]
    prev = result[0]
    for item in items[1:]:
        if not compare(prev, item):
            result.append(insert_item)
        result.append(item)
        prev = item
    return result

If you need to modify a list in place, without using the memory for two lists, you can do slice assignment. I kind of dislike using the index and while loop here, but since we're modifying the list as we go, it seems the simplest way in this case. This will be slower, especially with large lists, but you'll also save the most memory with large lists.

def insert_between(items, insert_item, compare):
    i = 1
    while i < len(items):
        if not compare(items[i-1], items[i]):
            items[i:i] = [insert_item]
            i += 1
        i += 1
    return items

Upvotes: 2

Katriel
Katriel

Reputation: 123622

>>> def insert_between(iterable, fill, cond):
...     iterable = iter(iterable)
...     prev = next(iterable)
...     yield prev
...     for cur in iterable:
...             if cond(prev, cur):
...                     yield fill
...             yield cur
...             prev = cur
...
>>>
>>> list(insert_between([1,2,3,4,7,8,9,15,16], 0, lambda x,y: x + 1 != y))
[1, 2, 3, 4, 0, 7, 8, 9, 0, 15, 16]

This is pretty much as efficient as you'll get, because you're going to have to make one pass through the list anyway and this makes only one pass. Notice that it is a generator, so you need to cast it to a list if you need all the values at once.

Upvotes: 11

Related Questions