55

I have a list, a:

a = ['a','b','c']

and need to duplicate some values with the suffix _ind added this way (order is important):

['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']

I tried:

b = [[x, x + '_ind'] for x in a]
c = [item for sublist in b for item in sublist]
print (c)
['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']

Is there some better, more pythonic solution?

jezrael
  • 822,522
  • 95
  • 1,334
  • 1,252

6 Answers6

66

You could make it a generator:

def mygen(lst):
    for item in lst:
        yield item
        yield item + '_ind'

>>> a = ['a','b','c']
>>> list(mygen(a))
['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']

You could also do it with itertools.product, itertools.starmap or itertools.chain or nested comprehensions but in most cases I would prefer a simple to understand, custom generator-function.


With python3.3, you can also use yield from—generator delegation—to make this elegant solution just a bit more concise:

def mygen(lst):
    for item in lst:
        yield from (item, item + '_ind')
cs95
  • 379,657
  • 97
  • 704
  • 746
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • 5
    Might I suggest a list comprehension version? `list([((yield x), (yield (x + '_ind'))) for x in a]) ; ['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']` – cs95 Jul 15 '17 at 20:12
  • 18
    @cᴏʟᴅsᴘᴇᴇᴅ Wtf... never seen such "inline" yield. Played with it some more, this works as well: `list([(yield from (x, x + '_ind')) for x in a])`. – Stefan Pochmann Jul 15 '17 at 20:15
  • 2
    @StefanPochmann Killer. Want to post an answer with it? If not, I'll edit mine. :p – cs95 Jul 15 '17 at 20:16
  • @cᴏʟᴅsᴘᴇᴇᴅ You do it. I don't even know how that works :-) – Stefan Pochmann Jul 15 '17 at 20:16
  • At first I thought there was weird side effect shenanigans happening here, but no...I don't think so...damn..this is nice. – idjaw Jul 15 '17 at 20:18
  • 3
    @StefanPochmann It's py3k only. They added yield support for list comps to allow generators to be created. – cs95 Jul 15 '17 at 20:19
  • 1
    @idjaw Yeah, python is full of easter eggs like this. I didn't even know yields could exist inside list comprehensions, but lo and behold they can, and they result in generators, not lists :) – cs95 Jul 15 '17 at 20:20
  • @cᴏʟᴅsᴘᴇᴇᴅ lol. PyCharm is reporting this as a syntax error (but still runs fine) even with a py3 interpreter attached. I think I'll see if there is a bug reported for it already. – idjaw Jul 15 '17 at 20:22
  • 3
    @idjaw Oh wait, just saw the red underlines on my IDE too. xD – cs95 Jul 15 '17 at 20:27
  • This seems to be by far the best way: simple, readable and efficient, no need for any other approaches – Chris_Rands Jul 17 '17 at 08:04
29

It can be shortened a little bit by moving the options to the inner for loop in the list comprehension:

a = ['a','b','c']

[item for x in a for item in (x, x + '_ind')]
# ['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']
Psidom
  • 209,562
  • 33
  • 339
  • 356
15

Another alternative with splicing (Python2.x, 3.x):

result = [None] * len(a) * 2
result[::2], result[1::2] = a, map(lambda x: x + '_ind', a)

result
# ['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']
cs95
  • 379,657
  • 97
  • 704
  • 746
6

You can use itertools.chain():

import itertools

l = ['a','b','c']

new_list = list(itertools.chain.from_iterable([[i, i+"_ind"] for i in l]))

print new_list

Output:

['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • 4
    I would use [`chain.from_iterable`](https://docs.python.org/library/itertools.html#itertools.chain.from_iterable). That way you don't need the unpacking with `*` – MSeifert Jul 15 '17 at 19:59
  • 1
    Alt: `list(itertools.chain(*zip(a, [x + '_ind' for x in a])) )` or `list(itertools.chain(*zip(a, map(lambda x: x + '_ind', a))))` – cs95 Jul 15 '17 at 20:03
  • 1
    @cᴏʟᴅsᴘᴇᴇᴅ Agreed, but again, `chain.from_iterable` would IMHO be a bit cleaner :) `list(chain.from_iterable(zip(a, [i+'_ind' for i in a])))`. Not that it is particularly important. – miradulo Jul 15 '17 at 20:08
5

Before list comprehensions and generators were invented/became widespread, people used to think much simpler1:

>>> a = ['a', 'b', 'c']
>>> b = []
>>> for x in a: b.extend([x, x+'_ind'])
... 
>>> b
['a', 'a_ind', 'b', 'b_ind', 'c', 'c_ind']

* I don't mean that those constructs/tools are evil, just wanted to point out that there is a simple solution.

Leon
  • 31,443
  • 4
  • 72
  • 97
  • As far as I know were comprehensions and generators invented (implemented) to make these "constructs and tools" simpler (and definitely a lot faster). But I guess that's a quite opinion-based statement so YMMV. – MSeifert Jul 17 '17 at 16:56
4

Since you asked for "simple", I thought I'd throw this in (albeit, maybe not the pythonic way):

for i in mylist: 
    mylist1.append(i);
    mylist1.append(i + '_ind');
today
  • 32,602
  • 8
  • 95
  • 115
Eladian
  • 958
  • 10
  • 29