2

In the spirit of short code... I have a list of either numbers or empty strings and I want to map(int, mylist) without it failing.

mylist = "123//456".split("/") #['123', '', '456']
myints = <something to quickly convert the list>

EDIT I need to keep the same number of tokens. I.e. myints = [123, 0, 456].

I was thinking of something along the lines of map(lambda x: int(x) or 0, mylist), but of course this still throws a ValueError. Any ideas?


Another EDIT

Some results (timing for xrange(1000000))...

  • 2.03s l = map(lambda x: x.isdigit() and int(x) or 0, mylist)
  • 1.37s l = [x.isdigit() and int(x) or 0 for x in mylist]
  • 1.99s l = map(lambda x: int(x) if x.isdigit() else 0, mylist)
  • 1.35s l = [int(x) if x.isdigit() else 0 for x in mylist]
  • 1.24s l = [int(x or 0) for x in mylist]
  • 1.45s l = [int(e) if e.strip() else 0 for e in mylist]
  • 4.44s the big chunk of code with try/except ergo bigger is slower :) ok, not always

Just included these for their time as they don't produce the desired output...

  • 3.71s l = map(int, re.findall('[0-9]+', "123//456")) ('course regex is slow. time&place)
  • 1.47s l = map(int, filter(None, mylist))
  • 1.15s [int(x) for x in mylist if x] (nice time!)

*I should mention re.compile shaves 3.71s down to 2.11s.

Python 2.7.3, Xeon E5-1607

myints = [int(x or 0) for x in mylist] is not only the fastest with the correct result but also VERY short at 29 characters.

Thanks all for your input! Not so much the downvoters :P

5
  • @Pyson If only I could relax arbitrary restrictions, coding in general would be much simpler :) In this case I actually need to know which position the integer came from as the string may be "123/456/789". Commented Nov 1, 2013 at 7:32
  • There are other and more robust ways to do that. Commented Nov 1, 2013 at 7:39
  • @Pyson Feel free to share them/add an answer :) I'm interested to know! Commented Nov 1, 2013 at 7:44
  • @Pyson If "Silly restriction" is referring to try/except and not same-number-of-tokens, my timing results seem to suggest otherwise. Commented Nov 1, 2013 at 12:43
  • Perhaps you wouldn't have received the downvote had you put the valuable timing results in an answer rather than in the question, but I'm only guessing. Commented Mar 18, 2021 at 9:36

8 Answers 8

6

Short, though not as robust as other answers -- x must be an empty string or a number:

[int(x or 0) for x in mylist]
Sign up to request clarification or add additional context in comments.

1 Comment

very nice. without isdigit() overhead too
3

Short isn't always the best, but this is pretty short:

>>> [int(e) if e.strip() else 0 for e in "123//456".split("/")]
[123, 0, 456]

Also:

>>> map(int, (e if e.isdigit() else '0' for e in "123//456".split("/")))

1 Comment

Upvoting in particular for the second form, which shows a different use of map than the rest of the answers. The first form depends on the OP's statement that the input will only be digit strings or empty strings - it's not wrong as long as that constraint holds, but it's more vulnerable to bad input like "1/a/2" than the forms that use isdigit. How much that matters depends on how certain the OP is of the input format.
2
myints = map(lambda x: int(x) if x.isdigit() else 0, "123//456".split("/")) # returns [123, 0, 456]

As you wanted, it keeps the same numbers of tokens, and replaces non-digit strings with zeroes.

Edit: You can also do it via comprehensions:

myints = [int(x) if x.isdigit() else 0 for x in "123//456".split("/")]

2 Comments

Actually I quite liked your first idea, using the [... for ...] syntax: [x.isdigit() and int(x) or 0 for x in mylist].
@jozxyqk well, I've just shown you how you can do it via map function as it was your original idea, but yes, you can use comprehensions also.
2

This looks to me like a situation where a for loop would be more readable than a list comprehension. But if you really want to do it in as little code as possible, try this:

myints = [int(x) if x.isdigit() else 0 for x in mylist]

Obviously you could use any value you liked in place of 0 for non-digit strings, which will include empty strings.

The for loop form would be something like this:

myints = []
for x in mylist:
    try:
        intx = int(x)
    except (TypeError, ValueError):
        intx = 0
    finally:
        myints.append(intx)

That form has the advantage of working on any input that int can handle, with negative numbers leaping to mind - '-7'.isidigit() will return False, so any of the int(x) if x.isdigit() else 0 solutions will turn '123//-7' into [123, 0, 0] rather than [123, 0, -7]. Your problem statement doesn't address negative integers so I'm not sure what you need there.

Comments

1

Try using a list comprehension:

my_ints = [int(x) for x in my_str.split('/') if x]

Comments

1

Using regex:

>>> import re
>>> map(int, re.findall('[0-9]+', "123//456////1231231"))
[123, 456, 1231231]

If string is huge then you can use re.finditer and a list comprehension:

>>> [int(m.group()) for m in re.finditer('[0-9]+', "123//456////1231231")]
[123, 456, 1231231]

For your edit, you can use str.isdigit and a ternary expression:

>>> mylist = "123//456"
>>> [int(x) if x.isdigit() else 0 for x in mylist.split('/')]
[123, 0, 456]

Comments

0

Actually this is one way that works, but there is the extra call to .isdigit(), which doesn't seem like it should be necessary.

mylist = "123//456".split("/") #['123', '', '456']
myints = map(lambda x: x.isdigit() and int(x) or 0, mylist)

[123, 0, 456]

3 Comments

Using isdigit is good, but using and and or to simulate a ternary operator is both ugly and (as of Python 2.5) unnecessary: docs.python.org/2/reference/…
@PeterDeGlopper can you show the preferred way to write an inline if/then/else?
int(x) if x.isdigit() else 0 in place of x.isdigit() and int(x) or 0.
0

Why not split at the '//'?

print map(int, "123//456".split('//'))

If this isn't an option, then you can do:

print map(int, filter(None, mylist))

Or if you want to keep the same length:

print map(lambda x: int(x) if x.isdigit() else 0, mylist)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.