2

The goal is to insert a string after every other element in the list apart from the last one:

arr0 = ['aa','bb','cc','dd']

Goal

 ['aa','XX','bb', 'XX','cc','XX','dd']

This topic has been addressed in posts like this, but the lists of strings used are only one character in length which affects the list comprehension. I do not have enough reputation points to comment and ask for clarification.

I have implemented it with a for loop, but I was trying to practice with list-comprehension and would appreciate insight as to where I am going wrong with it. Currently getting a SyntaxError: invalid syntax.

Example and Current Implementation

arr0 = ['aa','bb','cc','dd'] # Goal ['aa','XX','bb', 'XX','cc','XX','dd']

# Stop for range
total = len(arr0)*2-1

for i in range(1, total, 2):
   arr0.insert(i, "XX")

# Returns ['aa', 'XX', 'bb', 'XX', 'cc', 'XX', 'dd']

List Comprehension Attempt:

[el for y in [[el, 'XX'] if idx != len(arr0)-1 else el for idx, el in enumerate(arr0)] for el in y if isinstance(y, list) else el]
Breakdown
[[el, 'XX'] if idx != len(arr0)-1 else el for idx, el in enumerate(arr0)]

# Returns
# [['aa', 'XX'], ['bb', 'XX'], ['cc', 'XX'], 'dd']

In the outer comprehension, I am trying to return it as a single list of the strings. I am trying to use isinstance to check if the element is a list or not (the last item being a string) and if not return simply the string.

Edit

I really appreciate the responses. I should have included this alternative case that I do encounter where I do not want elements inserted after a 'Note' element at the end, in which case I could not perform the slice. Is negative indexing with a step possible?

# Alternative scenario
arr1 = ['aa','bb','cc','dd', 'Note']

# ['aa','XX,'bb','XX,'cc','XX,'dd','Note']
0

2 Answers 2

5

You can simply use a nested list comprehension and strip the last element:

>>> [e for i in arr0 for e in [i, "XX"]][:-1]
['aa', 'XX', 'bb', 'XX', 'cc', 'XX', 'dd']

You can also use a .split()/.join() trick (which is probably less performant):

>>> ",XX,".join(arr0).split(",")
['aa', 'XX', 'bb', 'XX', 'cc', 'XX', 'dd']

A fancier way is to use itertools.chain:

>>> from itertools import chain
>>> list(chain.from_iterable(zip(arr0, ["XX"]*len(arr0))))[:-1]
['aa', 'XX', 'bb', 'XX', 'cc', 'XX', 'dd']

Edit: For the alternative case added later to the question, it is possible to slice the input and manually append the last element to the output:

>>> arr1 = ['aa','bb','cc','dd', 'Note']
>>> [e for i in arr1[:-1] for e in [i, "XX"]][:-1] + [arr1[-1]]
['aa', 'XX', 'bb', 'XX', 'cc', 'XX', 'dd', 'Note']
Sign up to request clarification or add additional context in comments.

5 Comments

If you add another import from itertools import zip_longest it might be shorter chain.from_iterable(zip_longest(arr0, [], fillvalue="XX"))
@frost-nzcr4 It is not shorter though. It would still leave a "XX" at the end that you'll need to strip out.
This part will shortened ["XX"]*len(arr0), anyway good answer)
Ah, true, it would certainly be cleaner, if not shorter. Care to post it as an alternative answer?
@Selcuk I was originally using the enumerate to be able to control how many times to create the [elem, 'XX'] due to the case in my edit. But the slicing definitely works for the other scenarios.
1

Just for fun, one more option to consider if you want all the logic within the list comprehension itself:

from itertools import zip_longest

initial_list = ['aa','bb','cc','dd'] # Goal ['aa','XX','bb', 'XX','cc','XX','dd']

padded_list = [
    value
    for item in zip_longest(initial_list, ["XX"] * (len(initial_list) - 1))
    for value in item
    if value
]

print(padded_list)

Output:

['aa', 'XX', 'bb', 'XX', 'cc', 'XX', 'dd']

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.