22

I want to add two items at a time to a list comprehension. One item is constant. How can this be achieved using only one for loop inside of a list comprehension, and no additional functions. Answers that do not use any import will be favored.

Take a look at at the following:

>>> mystring = 'ABCELKJSDLHFWEHSJDHFKHIUEHFSDF'
>>> sum([['^', char] for char in mystring.lower()], [])
['^', 'a', '^', 'b', '^', 'c', '^', 'e', '^', 'l', '^', 'k', '^', 'j', '^', 's', '^', 'd', '^', 'l', '^', 'h', '^', 'f', '^', 'w', '^', 'e', '^', 'h', '^', 's', '^', 'j', '^', 'd', '^', 'h', '^', 'f', '^', 'k', '^', 'h', '^', 'i', '^', 'u', '^', 'e', '^', 'h', '^', 'f', '^', 's', '^', 'd', '^', 'f']

I am trying to make a list with the character ^ prepended before each letter in lower case. In this example, you need to use sum to flatten the list. However, my question is, if it is possible to make a flat list in the first place. The output above is the desired output.

As in, append something constantly before a variable that changes with each iteration of the for loop. One cannot use two for loops here, as that would be too simple, for example:

mystring = 'ABCELKJSDLHFWEHSJDHFKHIUEHFSDF'
print [item for x in mystring.lower() for item in ['^', x]]

If one does something like this:

>>> mystring = 'ABCELKJSDLHFWEHSJDHFKHIUEHFSDF'
>>> [['^', x] for x in mystring]
['^', 'a', '^', 'b', '^', 'c', '^', 'e', '^', 'l', '^', 'k', '^', 'j', '^', 's', '^', 'd', '^', 'l', '^', 'h', '^', 'f', '^', 'w', '^', 'e', '^', 'h', '^', 's', '^', 'j', '^', 'd', '^', 'h', '^', 'f', '^', 'k', '^', 'h', '^', 'i', '^', 'u', '^', 'e', '^', 'h', '^', 'f', '^', 's', '^', 'd', '^', 'f']

You get lists within lists. Thus, is there a way that you can append two items at a time in a list comprehension without have to use an addition for loop or an additional function like sum? I ask this, because its something quite simple, yet I can't find a way to do it. If one tries to do the following:

>>> ['^', x for x in mystring.lower()]
  File "<console>", line 1
    ['^', x for x in mystring.lower()]
              ^
SyntaxError: invalid syntax

The attempt gives a SyntaxError. So, is what I'm asking impossible to do in Python? Using () gives me a list of tuples.

I've also tried using the splat/unpacking operator:

>>> [*['^', x] for x in mystring.lower()]
  File "<console>", line 1
    [*['^', x] for x in mystring.lower()]
     ^
SyntaxError: invalid syntax

But as above, this too is a syntax error. Sorry for this late edit, but I have tried the following:

import itertools
mystring = "HELLOWORLD"
print(list(itertools.chain.from_iterable(('^', x) for x in mystring.lower())))

But the above still required an import.

Games Brainiac
  • 80,178
  • 33
  • 141
  • 199
  • What is your expected output? Do you want `^` as separate list element, before each character, or just appended to the character. – Rohit Jain Oct 19 '13 at 12:48
  • Under the first code sample, the the output is given. I think its fairly clear. – Games Brainiac Oct 19 '13 at 12:50
  • 2
    Same question here - http://stackoverflow.com/questions/11868964/python-list-comprehension-returning-two-or-more-items-for-each-item – Tom Ron Oct 19 '13 at 12:52

4 Answers4

12

You can use itertools.chain.from_iterable, this is equivalent to that nested list comprehension version but slightly efficient(for large lists):

>>> from itertools import chain
>>> mystring = 'ABCELKJSDLHFWEHSJDHFKHIUEHFSDF'
>>> list(chain.from_iterable([['^', x] for x in mystring]))
['^', 'A', '^', 'B', '^', 'C', '^', 'E', '^', 'L', '^', 'K', '^', 'J', '^', 'S', '^', 'D', '^', 'L', '^', 'H', '^', 'F', '^', 'W', '^', 'E', '^', 'H', '^', 'S', '^', 'J', '^', 'D', '^', 'H', '^', 'F', '^', 'K', '^', 'H', '^', 'I', '^', 'U', '^', 'E', '^', 'H', '^', 'F', '^', 'S', '^', 'D', '^', 'F']

In Python 3.3+ you can also use yield from in a generator function:

>>> mystring = 'ABCELKJSDLHFWEHSJDHFKHIUEHFSDF'
>>> def solve(strs):
...     for x in strs:
...         yield from ['^', x]
...         
>>> list(solve(mystring))
['^', 'A', '^', 'B', '^', 'C', '^', 'E', '^', 'L', '^', 'K', '^', 'J', '^', 'S', '^', 'D', '^', 'L', '^', 'H', '^', 'F', '^', 'W', '^', 'E', '^', 'H', '^', 'S', '^', 'J', '^', 'D', '^', 'H', '^', 'F', '^', 'K', '^', 'H', '^', 'I', '^', 'U', '^', 'E', '^', 'H', '^', 'F', '^', 'S', '^', 'D', '^', 'F']
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • Is it possible without `itertools`, because I've tried this as well. – Games Brainiac Oct 19 '13 at 12:54
  • @GamesBrainiac `sum()` is highly inefficient for this([also not recommended for this purpose](http://docs.python.org/2/library/functions.html#sum)), and `itertools.chain` will surely beat nested LC solution for larger lists. – Ashwini Chaudhary Oct 19 '13 at 12:57
  • I understand this, but is there no way to add one constant every time to a list comprehension? This feels like it _should_ be there. You are using imports, but its seems strange that something _this_ basic is not available. I understand that `sum` is inefficient, but that is besides the point, I just wanted to show what I've tried. Is there no way to use the `splat` operator in a creative way to get what I want? – Games Brainiac Oct 19 '13 at 13:01
  • @GamesBrainiac No splat operator will not work here. – Ashwini Chaudhary Oct 19 '13 at 13:03
  • So, I guess this can't be done without imports or nested for loops? – Games Brainiac Oct 19 '13 at 13:13
  • @GamesBrainiac Yes, but you can use perreal's first solution if both `'^'` and `x` are both single length characters. – Ashwini Chaudhary Oct 19 '13 at 13:16
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/39558/discussion-between-games-brainiac-and-hcwhsa) – Games Brainiac Oct 19 '13 at 13:17
8

You can start with this:

print list( '^'.join(mystring.lower()) )

which gives:

['a', '^', 'b', '^', 'c', '^', ...]

So this would give the desired output:

l = list( '^'.join(mystring.lower()) )
l.insert(0, '^')
print l

And another way:

print [ y for x in zip(['^'] * len(mystring), mystring.lower()) for y in x ]

which gives:

['^', 'a', '^', 'b', '^', 'c', ...
perreal
  • 94,503
  • 21
  • 155
  • 181
0

If anyone's using pydash, you can use flatten_deep. Basically, it recursively flattens your list down to one level so you can do a list comprehension of multiple lists.

>>> import pydash
>>> mystring = 'ABCELKJSDLHFWEHSJDHFKHIUEHFSDF' 
>>> pydash.flatten_deep([['^', x] for x in mystring])
['^', 'A', '^', 'B', '^', 'C', '^', 'E', '^', 'L', '^', 'K', '^', 'J', '^', 'S',           '^', 'D', '^', 'L', '^', 'H', '^', 'F', '^', 'W', '^', 'E', '^', 'H', '^', 'S', '^', 'J', '^', 'D', '^', 'H', '^', 'F', '^', 'K', '^', 'H', '^', 'I', '^', 'U', '^', 'E', '^', 'H', '^', 'F', '^', 'S', '^', 'D', '^', 'F']
Alec Gerona
  • 2,806
  • 1
  • 24
  • 24
0

Well, in this specific case...

>>> list('abcd'.replace('', '^')[ : -1])
['^', 'a', '^', 'b', '^', 'c', '^', 'd']
>>>