2

is it possible to write list comprehensions for the following python code:

for str in range(0,len(mixed_content)):
    if (mixed_content[str].isdigit()):
        num_list.append(mixed_content[str])
    else:
        string_list.append(mixed_content[str])

can we use else block in list comprehensions ? I tried to write list comprehensions for above code :

num_list , string_list = [   mixed_content[str]  for str in range(0,len(mixed_content)) if(mixed_content[str].isdigit()) else ]
Thomas K
  • 39,200
  • 7
  • 84
  • 86
robert williams
  • 747
  • 2
  • 11
  • 18

6 Answers6

6

It's not possible as-is, but if you're looking for one-liners you can do that with a ternary expression inside your loop (saves a test and is compact):

num_list=[]
string_list=[]
for s in ["45","hello","56","foo"]:
    (num_list if s.isdigit() else string_list).append(s)

print(num_list,string_list)

result:

['45', '56'] ['hello', 'foo']

Notes:

  • despite the parenthesized syntax and the context of the question, (num_list if s.isdigit() else string_list) is not a generator, a ternary expression (protected between parentheses to counter .append precedence) which returns num_list if s is a sequence of (positive) digits and string_list if s otherwise.
  • this code won't work with negative numbers because isdigits will return false. You'll have to code a special function for that (classical problem here on SO)
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • Much appreciated. I knew the component parts but never seen it assembled like this (in one line and with a `.append()` on the end) so it seemed a bit like magic :). – roganjosh Oct 19 '16 at 20:00
2

You can only construct one list at a time with list comprehension. You'll want something like:

nums = [foo for foo in mixed_list if foo.isdigit()]
strings = [foo for foo in mixed_list if not foo.isdigit()]
Will
  • 4,299
  • 5
  • 32
  • 50
  • 1
    While this solves the problem, this is one of the cases when list comprehension is at disadvantage against regular loop - because you loop twice over data, and check each value twice. – volcano Oct 20 '16 at 15:42
2

Here is an example of using x if b else y in a list comprehension.

mixed_content = "y16m10"

num_list, string_list = zip(
    *[(ch, None) if ch.isdigit() else (None, ch) for ch in mixed_content])
num_list = filter(None, num_list)
string_list = filter(None, string_list)
print num_list, string_list
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • 1
    I was also going to post the *padding* with `None` and then *filter* later, but I figured I'll be running 3 iterations (or even more with the transpose) on the list length. Witty one though (+1) – Moses Koledoye Oct 19 '16 at 19:59
  • I like this idea of using the `zip(*` to turn a list of tuples into the tuple of lists. For some problems this might a good way of generating 2 lists from a single comprehension. But here we are stuck with the extra step of filtering out the `None`. – hpaulj Oct 19 '16 at 20:27
1

Let's initialize variables:

>>> mixed_content='ab42c1'; num_list=[]; string_list=[]

Because the OP asked about using "if-else in python list comprehensions," let's show that that can be done:

>>> [num_list.append(c) if c.isdigit() else string_list.append(c) for c in mixed_content]
[None, None, None, None, None, None]

Let's verify that we have the lists that you want:

>>> num_list, string_list
(['4', '2', '1'], ['a', 'b', 'c'])
John1024
  • 109,961
  • 14
  • 137
  • 171
  • 1
    While this does technically work, it's usually a bad idea to use a list comprehension when all you really want is a loop. Just write a `for` loop instead and you won't get a list of `None` values. – Blckknght Oct 19 '16 at 19:52
  • Technically, the commands in the `for` loop still return `None` values – John1024 Oct 19 '16 at 19:57
  • 1
    Sure, it's building a list out of the `None`s that's wasteful. – Blckknght Oct 19 '16 at 19:58
  • 1
    In simple time tests, this list comprehension is only slightly slower than the OP's loop. I'm bothered more by the use of a list comprehension for its side effects (the appends) rather than its primary output. It violates the spirit, if not the letter, of list comprehensions. – hpaulj Oct 19 '16 at 20:22
0

You can accomplish what you want with two list comprehensions:

num_list = [num for num in mixed_content if num.isdigit()]
string_list = [string for string in mixed_content if not string.isdigit()]

The else clause is not supported in list comprehensions:

>>> [c for c in range(5) if c == 1 else 0]
SyntaxError: invalid syntax
Billy
  • 5,179
  • 2
  • 27
  • 53
0

A super messy and unpractical way of doing is:

mixed_content = ['a','b','c',"4"]
string_list = []

print [y for y in [x if mixed_content[x].isdigit() else string_list.append(mixed_content[x]) for x in range(0,len(mixed_content))] if y != None]
print string_list

Returns:

[3]
['a', 'b', 'c']

Basically list comprehension for your condition and then filter that list comprehension to only accept non None

MooingRawr
  • 4,901
  • 3
  • 24
  • 31