Reputation: 347
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
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
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
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
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
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