3

I want to replace an element in a list with multiple elements. So for example I have the list a = ['b', 'c']and want to replace 'b' with 'd', 'e' which would give me the list a = ['d', 'e', 'c'].

I have the following code: a = [??? if item == 'b' else item for item in a]. How do I proceed? I would love to keep the list comprehension, as this seems to be more suitable than a.extend(('b', 'c')), for example.

A. A. Pedersen
  • 161
  • 1
  • 1
  • 8
  • 1
    A list comprehension alone is not going to work, it cannot produce a list with more items than the input. – mkrieger1 Nov 17 '18 at 22:55
  • 4
    a lot of people will say create nested lists and flatten them, but i actually think a regular for loop with list.extend can be *more* idiomatic – Chris_Rands Nov 17 '18 at 23:01
  • 1
    the most idiomatic here is probably what runs the fastest. Creating lists in list comprehension just to be able to provide 1 or several elements surely wastes CPU time – Jean-François Fabre Nov 17 '18 at 23:02
  • @Jean-FrançoisFabre this might deserve a seperate Q&A with a decent benchmark, or perhaps the question already exists? This dosen't quite hit it https://stackoverflow.com/questions/3899645/list-extend-and-list-comprehension – Chris_Rands Nov 17 '18 at 23:04
  • I wouldn't be surprised it if did. – Jean-François Fabre Nov 17 '18 at 23:04

5 Answers5

4

Another option is to write a generator function:

def replace_item(the_list):
    for item in the_list:
        if item == 'b':
            yield 'd'
            yield 'e'
        else:
            yield item
>>> list(replace_item(['a', 'b']))
['a', 'd', 'e']
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
3

Perhaps you can create a 2d list and then flatten it with itertools.chain:

from itertools import chain

a = ['b', 'c']
a = list(chain.from_iterable(['d', 'e'] if item == 'b' else [item] for item in a))

print(a)
# ['d', 'e', 'c']
slider
  • 12,810
  • 1
  • 26
  • 42
2

Another approach:

>>> v = ['b', 'c']
>>> v = [a for b in v for a in (['d', 'e'] if b == 'b' else [b])]
>>> v
['d', 'e', 'c']

You can easily generalize this to multiple replacements:

>>> v = ['b', 'c']
>>> rep = {'b': ['d', 'e']}
>>> v = [a for b in v for a in rep.get(b, [b])]
>>> v
['d', 'e', 'c']
arshajii
  • 127,459
  • 24
  • 238
  • 287
1

You can use the slice syntax to replace a number of elements with another:

a = 'a b c d b e'.split()
for i, item in list(enumerate(a))[::-1]:  # makes and copy and reverse it (IMPORTANT!)
    if item == 'b':
        a[i:i+1] = ['d', 'e']
print(a)

['a', 'd', 'e', 'c', 'd', 'd', 'e', 'e']

The reversing part is important because each replacement makes the list longer, when iterating backward the index should remain valid.

Yoav Glazner
  • 7,936
  • 1
  • 19
  • 36
0
def insert_in_list(a, item_to_replace, list_to_insert):
    try:
        index = a.index(item_to_replace)
    except ValueError: #if item is not in list
        return a
    a[index] = list_to_insert[0]
    for i in list_to_insert[1:]:
        a.insert(index + 1, i)
    return a

it's a bit long, but i don't know if there is a better way to insert.

iElden
  • 1,272
  • 1
  • 13
  • 26
  • 1
    It should be `list_to_insert[0]`, not `list_to_insert(1)` that gives an error, as you try to call the list. Furthermore, `insert_in_list(['b', 'c'], 'b', ['e', 'f', 'g', 'h'])` returns `['e', 'h', 'g', 'f', 'c']`: the order of the inserted list is reversed. Also, this will fail if there is more than one instance of the value to replace. – Thierry Lathuille Nov 17 '18 at 23:23