Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - store a string and an int using map(sys.stdin.readline())

If the input contains a space separated line of int, like-

1 3

I can map store it in an array using the map() function

arr = map(int,sys.stdin.readline().split())

or even in two separate variables, by

n,m = map(int,sys.stdin.readline().split())

Is there any way to use the same way to read an input line that contains mixed data types. eg.-

foo 3

where foo is a string and 3 is an integer?

like image 626
deathstroke Avatar asked May 22 '15 22:05

deathstroke


4 Answers

If you always had a string and non negative int:

import sys
n, m = map(lambda x: (str, int)[x.isdigit()](x) ,sys.stdin.readline().split(None, 1)) 


print(n,m)

But the safest way is always to use a try/except when casting user input even when only expecting one type.

As requested checking for negative is possible:

import sys
n, m = map(lambda x: (str, int)[x.isdigit() or x.strip("-").isdigit()](x) ,sys.stdin.readline().split())


print(n, m)

But --10 --10-- would also be pass the test but cause an error so again only for your specific case.

like image 78
Padraic Cunningham Avatar answered Oct 27 '22 00:10

Padraic Cunningham


To do that you should be able to discriminate between strings that can represent integers and strings that cannot. An example is:

def foo(s):
    try:
        return int(s)
    except ValueError:
        return s

Then you can normally use map:

map(foo, sys.stdin.readline().split())

The above line for input:

abcdef 110

will print:

['abcdef', 110]
like image 36
JuniorCompressor Avatar answered Oct 26 '22 23:10

JuniorCompressor


You could use str.isdigit to test whether the string can be cast to an integer number.

>>> inpt = "foo 3"
>>> [int(s) if s.isdigit() else s for s in inpt.split()]

Of course, you can do the same using map and sys.stdin.readline using a lambda

>>> map(lambda s: int(s) if s.isdigit() else s, sys.stdin.readline().split())
foo 4
['foo', 4]

If you want to support all sorts of data types, you can try to literal_eval and fall back to the basic string if that does not work.

import ast
def try_eval(string):
    try:
        return ast.literal_eval(string)
    except ValueError:
        return string

>>> map(try_eval, "42 3.14 foo 'bar' [1,2,3]".split())
[42, 3.1400000000000001, 'foo', 'bar', [1, 2, 3]]
like image 24
tobias_k Avatar answered Oct 27 '22 00:10

tobias_k


map is for when you want to apply the same transformation to every element of the input. That doesn't fit your use case; you want to treat the two inputs in different ways. Since the data has a fixed format of string, then integer, it'd be best to parse it in a way that always produces that format:

x, y = raw_input().split()
y = int(y)

If you have more columns, you could make a list of which function to use to handle each column:

handlers = [str, int, int, str, float, int, int]
a, b, c, d, e, f, g = [f(x) for (f, x) in zip(handlers, raw_input().split())]

The solutions suggested by the other answers don't account for the fixed format of the input. If the user inputs

31 42

x should be "31", not 31, and if the user inputs

foo bar

that should be detected as an error, rather than assigning "bar" to y.

like image 29
user2357112 supports Monica Avatar answered Oct 26 '22 23:10

user2357112 supports Monica