Insert element in Python list after every nth element

Question:

Say I have a Python list like this:

letters = ['a','b','c','d','e','f','g','h','i','j']

I want to insert an ‘x’ after every nth element, let’s say three characters in that list. The result should be:

letters = ['a','b','c','x','d','e','f','x','g','h','i','x','j']

I understand that I can do that with looping and inserting. What I’m actually looking for is a Pythonish-way, a one-liner maybe?

Asked By: QuestionEverything

||

Answers:

Try this

i = n
while i < len(letters):
    letters.insert(i, 'x')
    i += (n+1)

where n is after how many elements you want to insert 'x'.

This works by initializing a variable i and setting it equal to n. You then set up a while loop that runs while i is less then the length of letters. You then insert 'x' at the index i in letters. Then you must add the value of n+1 to i. The reason you must do n+1 instead of just n is because when you insert an element to letters, it expands the length of the list by one.

Trying this with your example where n is 3 and you want to insert 'x', it would look like this

letters = ['a','b','c','d','e','f','g','h','i','j']
i = 3
while i < len(letters):
    letters.insert(i, 'x')
    i += 4

print letters

which would print out

['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j']

which is your expected result.

Answered By: michaelpri
l = ['a','b','c','d','e','f','g','h','i','j']
[ l.insert(n+(n+1)*i, 'x') for i in range(len(l)/n) ]
print l
Answered By: hongshuning

A pretty straightforward method:

>>> letters = ['a','b','c','d','e','f','g','h','i','j']
>>> new_list = []
>>> n = 3
>>> for start_index in range(0, len(letters), n):
...     new_list.extend(letters[start_index:start_index+n])
...     new_list.append('x')
... 
>>> new_list.pop()
'x'
>>> new_list
['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j']

You can also use the grouper recipe from the itertools documentation for the chunking.

Answered By: Mike Graham

I’ve got two one liners.

Given:

>>> letters = ['a','b','c','d','e','f','g','h','i','j']
  1. Use enumerate to get index, add 'x' every 3rd letter, eg: mod(n, 3) == 2, then concatenate into string and list() it.

    >>> list(''.join(l + 'x' * (n % 3 == 2) for n, l in enumerate(letters)))
    
    ['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j']
    

    But as @sancho.s points out this doesn’t work if any of the elements have more than one letter.

  2. Use nested comprehensions to flatten a list of lists(a), sliced in groups of 3 with 'x' added if less than 3 from end of list.

    >>> [x for y in (letters[i:i+3] + ['x'] * (i < len(letters) - 2) for
         i in xrange(0, len(letters), 3)) for x in y]
    
    ['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j']
    

(a) [item for subgroup in groups for item in subgroup] flattens a jagged list of lists.

Answered By: Mark Mikofski

Although using list.insert() in a for loop seems to be more memory efficient, in order to do it in one-line, you can also append the given value at the end of every equally divided chunks split on every nth index of the list.

>>> from itertools import chain

>>> n = 2
>>> ele = 'x'
>>> lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

>>> list(chain(*[lst[i:i+n] + [ele] if len(lst[i:i+n]) == n else lst[i:i+n] for i in xrange(0, len(lst), n)]))
[0, 1, 'x', 2, 3, 'x', 4, 5, 'x', 6, 7, 'x', 8, 9, 'x', 10]
Answered By: Ozgur Vatansever

I want to add a new element per item.

How about this ?

a=[2,4,6]
for b in range (0,len(a)):
    a.insert(b*2,1)

a is now

[1, 2, 1, 4, 1, 6]
Answered By: Tim Seed

This is an old topic, but it lacks the easiest, most “pythonic” solution, imo. It is no more than an extension to part 2 of Mark Mikofski‘s accepted answer that arguably improves readability (and therefore makes it more pythonic).

>>> letters = ['a','b','c','d','e','f','g','h','i','j']
>>> [el for y in [[el, 'x'] if idx % 3 == 2 else el for 
     idx, el in enumerate(letters)] for el in y]

['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j']
Answered By: John Paper

While Mark Mikofski‘s answer works, there is a faster solution by assigning slices:

import string

# The longer the list the more speed up for list3
# letters = ['a','b','c','d','e','f','g','h','i','j']
letters = list(string.ascii_letters)
print("org:", letters)

# Use enumerate to get index, add 'x' every 3rd letter, eg: mod(n, 3) == 2, then concatenate into string and list() it.
list1 = list(''.join(l + 'x' * (n % 3 == 2) for n, l in enumerate(letters)))
print("list1:", list1)
%timeit list(''.join(l + 'x' * (n % 3 == 2) for n, l in enumerate(letters)))

# But as @sancho.s points out this doesn't work if any of the elements have more than one letter.
# Use nested comprehensions to flatten a list of lists(a), sliced in groups of 3 with 'x' added if less than 3 from end of list.
list2 = [x for y in (letters[i:i+3] + ['x'] * (i < len(letters) - 2) for i in range(0, len(letters), 3)) for x in y]
print("list2:", list2)
%timeit [x for y in (letters[i:i+3] + ['x'] * (i < len(letters) - 2) for i in range(0, len(letters), 3)) for x in y]

# Use list slice assignments
len_letters = len(letters)
len_plus_x = ll // 3
list3 = [None for _ in range(len_letters + len_plus_x)]
list3[::4] = letters[::3]
list3[2::4] = letters[2::3]
list3[1::4] = letters[1::3]
list3[3::4] = ['x' for _ in range(len_plus_x)]
print("list3:", list3)
%timeit ll = len(letters); lp = ll//3; new_letters = [None for _ in range(ll + lp)]; new_letters[::4] = letters[::3]; new_letters[2::4] = letters[2::3]; new_letters[1::4] = letters[1::3]; new_letters[3::4] = ['x' for _ in range(lp)]

produces (using jupyter notebook)

org: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
list1: ['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j', 'k', 'l', 'x', 'm', 'n', 'o', 'x', 'p', 'q', 'r', 'x', 's', 't', 'u', 'x', 'v', 'w', 'x', 'x', 'y', 'z', 'A', 'x', 'B', 'C', 'D', 'x', 'E', 'F', 'G', 'x', 'H', 'I', 'J', 'x', 'K', 'L', 'M', 'x', 'N', 'O', 'P', 'x', 'Q', 'R', 'S', 'x', 'T', 'U', 'V', 'x', 'W', 'X', 'Y', 'x', 'Z']
13 µs ± 1.09 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
list2: ['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j', 'k', 'l', 'x', 'm', 'n', 'o', 'x', 'p', 'q', 'r', 'x', 's', 't', 'u', 'x', 'v', 'w', 'x', 'x', 'y', 'z', 'A', 'x', 'B', 'C', 'D', 'x', 'E', 'F', 'G', 'x', 'H', 'I', 'J', 'x', 'K', 'L', 'M', 'x', 'N', 'O', 'P', 'x', 'Q', 'R', 'S', 'x', 'T', 'U', 'V', 'x', 'W', 'X', 'Y', 'x', 'Z']
13.7 µs ± 336 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
list3: ['a', 'b', 'c', 'x', 'd', 'e', 'f', 'x', 'g', 'h', 'i', 'x', 'j', 'k', 'l', 'x', 'm', 'n', 'o', 'x', 'p', 'q', 'r', 'x', 's', 't', 'u', 'x', 'v', 'w', 'x', 'x', 'y', 'z', 'A', 'x', 'B', 'C', 'D', 'x', 'E', 'F', 'G', 'x', 'H', 'I', 'J', 'x', 'K', 'L', 'M', 'x', 'N', 'O', 'P', 'x', 'Q', 'R', 'S', 'x', 'T', 'U', 'V', 'x', 'W', 'X', 'Y', 'x', 'Z']
4.86 µs ± 35.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Answered By: ChrisFreeman

It’s worth stating the simple implementation too:

letters = ['a','b','c','d','e','f','g','h','i','j']

i = 3   #initial step
while i < len(letters):
    letters.insert(i,'x')
    i = i + 3 + 1  #increment step by one for every loop to account for the added element

It does use basic looping and inserting, but it also looks much simpler and comfortable to read than the one-liner examples, which IMHO makes it more Pythonish as requested in the first place.

Answered By: Sorin

The for loop already got the option to step up/down with a certain value:

letters = ['a','b','c','d','e','f','g','h','i','j']
n = 3

for i in range ( n, len(letters)+n, n+1 ):
  letters.insert ( i, 'X' )

print ( letters )

It does not need divisions or modulo operations, just additions and one size calculation. Output:

['a', 'b', 'c', 'X', 'd', 'e', 'f', 'X', 'g', 'h', 'i', 'X', 'j']
Answered By: areop-enap

I would decompose the problem into two problems. First break the list into chunks, then join them with whatever other thing you want to insert.

from itertools import chain

def join_at(elements: list, item: object, n: int) -> list:
    # lambda for inserting the element when chunking
    insert = lambda i: [] if i == 0 else [item]
    # chunk up the original list based on where n lands, inserting the element along the way
    chunked = [insert(i) + elements[i:i+n] for i in range(0, len(elements), n)]
    # flatten the chunks back out
    return list(chain(*chunked))

print(join_at([0,1,2,3,4,5,6,7,8,9], 'x', 3))

This will output the original list of int inserting an x at every 3rd position:

[0, 1, 2, 'x', 3, 4, 5, 'x', 6, 7, 8, 'x', 9]

Answered By: Kevin Kreiser
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.