40

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?

1

11 Answers 11

22

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.

Sign up to request clarification or add additional context in comments.

1 Comment

The first one does not work if elements are not single characters. It ends up with a list with each element being a single character (splitting strings).
19

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.

6 Comments

This is unnecessarily quadratic, which may or may not matter in this case.
@MikeGraham How is this quadratic? I would think that it is linear? (not trying to contradict you, just very bad with time complexity)
@michaelpri insert() itself takes O(n), used within a while loop makes it quadratic. Here is the wiki showing time complexity of each operation.
@ozgur Oh, I didn't know insert was O(n). Thanks :)
@MikeGraham In the OPs example, this being quadratic doesn't matter much, if they do keep it simple like this, then it won't matter, but as the list gets longer and longer, time will start to play a role
|
11

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]

1 Comment

Nicely done - easy to understand solution
6

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]

1 Comment

+1 for itertools.chain method, makes an iterable of [item for subgroup in group for item in subgroup] which is always so hard to remember. Also +1 for using ternary in a list comprehension; for some reason I hadn't thought of that, brilliant!
4

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.

2 Comments

What if new_list.pop() pops a value that's actually supposed to be there?
@Navith, it pops off the last value, which was the 'x' I appended last.
3

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']

Comments

3

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']

Comments

2

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.

Comments

1

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)

Comments

0

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]

Comments

-2
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.