4

I would like to convert the following string:

s = '1|2|a|b'

to

[1, 2, 'a', 'b']

Is it possible to do the conversion in one line?

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
wannik
  • 12,212
  • 11
  • 46
  • 58
  • What about more complex data types? How many data types do you expect? Have you tried anything? Any specific problems with your code? Why are you trying to do this in one line? – vaultah Dec 29 '15 at 16:36
  • @vaultah at first, just `int` and `string` but I'm thinking about how to use the solution for `float` too. – wannik Dec 29 '15 at 16:41

5 Answers5

13

Is it possible to do the conversion in one line?

YES, It is possible. But how?

Algorithm for the approach

  • Split the string into its constituent parts using str.split. The output of this is

    >>> s = '1|2|a|b'
    >>> s.split('|')
    ['1', '2', 'a', 'b']
    
  • Now we have got half the problem. Next we need to loop through the split string and then check if each of them is a string or an int. For this we use

  • The list comprehension can be easily written as [i for i in s.split('|')]. But how do we add an if clause there? This is covered in One-line list comprehension: if-else variants. Now that we know which all elements are int and which are not, we can easily call the builtin int on it.

    Hence the final code will look like

      [int(i) if i.isdigit() else i for i in s.split('|')]
    

Now for a small demo,

>>> s = '1|2|a|b'
>>> [int(i) if i.isdigit() else i for i in s.split('|')]
[1, 2, 'a', 'b']

As we can see, the output is as expected.


Note that this approach is not suitable if there are many types to be converted.

Community
  • 1
  • 1
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
7

You cannot do it for negative numbers or lots of mixed types in one line but you could use a function that would work for multiple types using ast.literal_eval:

from ast import  literal_eval
def f(s, delim):
    for ele in s.split(delim):
        try:
            yield literal_eval(ele)
        except ValueError:
            yield ele

s = '1|-2|a|b|3.4'

print(list(f(s,"|")))
[1, -2, 'a', 'b', 3.4]
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • Thank you for suggesting `ast.literal_eval`. It is very useful. I can now convert to many types using two lines: `from ast import literal_eval` and `[literal_eval(e) if e[-1].isdigit() else e for e in s.split('|')]` – wannik Dec 29 '15 at 17:00
  • @wannik no worries, if you wanted a single line you could try `list(map(literal_eval, re.sub('(?!\|)([A-Za-z]+)', "'" + r"\1" + "'", s).split("|"))))`, wrapping the characters in quotes but the function approach is a lot more robust – Padraic Cunningham Dec 29 '15 at 17:03
2

Another way, is using map built-in method:

>>> s='1|2|a|b'
>>> l = map(lambda x: int(x) if x.isdigit() else x, s.split('|'))
>>> l
[1, 2, 'a', 'b']

If Python3, then:

>>> s='1|2|a|b'
>>> l = list(map(lambda x: int(x) if x.isdigit() else x, s.split('|')))
>>> l
[1, 2, 'a', 'b']

Since map in Python3 would give a generator, so you must convert it to list

Iron Fist
  • 10,739
  • 2
  • 18
  • 34
1

It is possible to do arbitrarily many or complex conversions "in a single line" if you're allowed a helper function. Python does not natively have a "convert this string to the type that it should represent" function, because what it "should" represent is vague and may change from application to application.

def convert(input):
    converters = [int, float, json.loads]
    for converter in converters:
        try:
            return converter(input)
        except (TypeError, ValueError):
            pass
    # here we assume if all converters failed, it's just a string
    return input

s = "1|2.3|a|[4,5]"
result = [convert(x) for x in s.split("|")]
GrandOpener
  • 1,943
  • 1
  • 17
  • 25
1

If you have all kinds of data types(more than str and int), I believe this does the job.

s = '1|2|a|b|[1, 2, 3]|(1, 2, 3)'
print [eval(x) if not x.isalpha() else x for x in s.split("|")]

# [1, 2, 'a', 'b', [1, 2, 3], (1, 2, 3)]

This fails if there exists elements such as "b1"

Rockybilly
  • 2,938
  • 1
  • 13
  • 38