noibe
noibe

Reputation: 347

Adding an element to a list every second element

I want to insert an element to an existing list every second element.

Let's say the list is fruits = ["banana", "apple", "mango", "kiwi"], to add the element "peach" what I do is:

fruits = ["banana", "apple", "mango", "kiwi"]
fruits_2 = list(fruits)

for i in range(len(fruits)):
    fruits_2.insert(2*i + 1, "peach")

print(fruits_2)

and the output is

['banana', 'peach', 'apple', 'peach', 'mango', 'peach', 'kiwi', 'peach']

which is what I want, but I feel like there is probably a better and more concise way to do this, without creating a second list.

Upvotes: 3

Views: 1295

Answers (5)

floupinette
floupinette

Reputation: 312

To insert an element in a list every second element, i recommend this code :

fruits = ["banana", "apple", "mango", "kiwi"]

for x in range(len(fruits), 0 , -1):
    fruits.insert(x, "peach")

You'll get this result

['banana', 'peach', 'apple', 'peach', 'mango', 'peach', 'kiwi', 'peach']

Don't try to make a comprehension list from this code. It will not work.

Upvotes: 0

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

Simply with "double-length" (and even/odd position) magic:

fruits = ["banana", "apple", "mango", "kiwi"]
new_fruits = [fruits[i // 2] if i % 2 == 0 else 'peach' for i in range(len(fruits) * 2)]
print(new_fruits)

The output:

['banana', 'peach', 'apple', 'peach', 'mango', 'peach', 'kiwi', 'peach']

If proceeding with itertools approaches itertools.zip_longest feature seems to be the most fast:

new_fruits = [v for v in itertools.zip_longest(fruits, add, fillvalue=add[0])]

Timings:

In [33]: fruits = ["banana", "apple", "mango", "kiwi"]                                                      

In [34]: add = ["peach"]                                                                                    

In [35]: %timeit [v for v in itertools.zip_longest(fruits, add, fillvalue=add[0])]                          
859 ns ± 16.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [36]: %timeit list(itertools.chain.from_iterable(zip(fruits, itertools.repeat(*add))))                   
992 ns ± 40.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [37]: %timeit list(itertools.chain(*itertools.product(fruits, add)))                                     
1.05 µs ± 18 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [38]: %timeit [i for s in [[f, *add] for f in fruits] for i in s]                                        
1.25 µs ± 73.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [39]: %timeit [fruits[i // 2] if i % 2 == 0 else "peach" for i in range(len(fruits) * 2)]                
1.29 µs ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Upvotes: 2

Almog
Almog

Reputation: 27

Exactly as you did but on the first list:

fruits = ["banana", "apple", "mango", "kiwi"]
for i in range(len(fruits)):
    fruits.insert(i*2+1, "peach")

>>> fruits
['banana', 'peach', 'apple', 'peach', 'mango', 'peach', 'kiwi', 'peach']

Upvotes: 0

FObersteiner
FObersteiner

Reputation: 25544

list comprehension + flattening option - no imports needed:

fruits = ["banana", "apple", "mango", "kiwi"]
add = 'peach'
out = [i for s in [[f, add] for f in fruits] for i in s]
out
# ['banana', 'peach', 'apple', 'peach', 'mango', 'peach', 'kiwi', 'peach']

itertools product option:

fruits = ["banana", "apple", "mango", "kiwi"]
add = ["peach"]
out = list(itertools.chain(*itertools.product(fruits, add)))
out
# ['banana', 'peach', 'apple', 'peach', 'mango', 'peach', 'kiwi', 'peach']

some timings:

%timeit list(chain.from_iterable(zip(fruits, repeat(*add))))
910 ns ± 19.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit list(itertools.chain(*itertools.product(fruits, add)))
1.07 µs ± 13.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit [fruits[i // 2] if i % 2 == 0 else "peach" for i in range(len(fruits) * 2)]
1.62 µs ± 40.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit [i for s in [[f, *add] for f in fruits] for i in s]
2.16 µs ± 630 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

however, the fastest option seems to be the simplest:

def func(fruits):
    result = []
    for x in fruits:
        result.append(x)
        result.append("peaches")
    return result

%timeit func(fruits)
746 ns ± 15.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Upvotes: 2

chepner
chepner

Reputation: 530922

Use zip, along with itertools.repeat and itertools.chain.

>>> from itertools import chain, repeat
>>> fruits = ["banana", "apple", "mango", "kiwi"]
>>> list(chain.from_iterable(zip(fruits, repeat("peach"))))
['banana', 'peach', 'apple', 'peach', 'mango', 'peach', 'kiwi', 'peach']

repeat creates an infinite sequence of 'peach'. zip creates a sequence of pairs, consisting of one item from fruits followed by an instance of 'peach'. chain.from_iterable "flattens" the sequence of pairs into a single sequence, and list produces a concrete list from the sequence.


Looking at the intermediate steps:

>>> from itertools import islice
>>> list(islice(repeat("peaches"), 5))
['peaches', 'peaches', 'peaches', 'peaches', 'peaches']

>>> zip(fruits, repeat("peaches"))
[('banana', 'peaches'), ('apple', 'peaches'), ('mango', 'peaches'), ('kiwi', 'peaches')]

This approach boils down to a series of appends to a new list, something like

result = []
for x in fruits:
    result.append(x)
    result.append("peaches")

But instead of hard-coding the arguments to append, you fetch them from a pair of iterators:

def peach_source():
    while True:
        yield "peach"

def fruit_source(fruits):
    for x in fruits:
        yield x

result = []
peaches = peach_source()
fruits = fruit_source(fruits)  # Yes, this is highly redundant; just showing the similarity to peach_source
done = False
while not done:
    try:
        result.append(next(fruits))
    except StopIteration:
        done = True
    result.append(next(peaches))

itertools.repeat creates peach_source for you. zip handle the alternation between fetching a fruit and fetching a peach. chain.from_iterable defines the action of appending to result, and list actually executes the appends.

Upvotes: 6

Related Questions