fvdalcin
fvdalcin

Reputation: 1047

How to transform a string into a list of char and int using a list comprehension in Python

Given the following string:

"[10,20]"

I want to create the following list using a list comprehension in Python:

['[', 10, ',', 20, ']']

Being 10 and 20 integers and the rest of the elements in the list chars.

I assume that I would need a to use something similar to what itertools.groupby(iterable, key=None) provides:

Make an iterator that returns consecutive keys and groups from the iterable. The key is a function computing a key value for each element. If not specified or is None, key defaults to an identity function and returns the element unchanged. Generally, the iterable needs to already be sorted on the same key function.

However Python's group by returns an iterator with consecutive keys and groups. In my case the keys would change so I guess I'd need a similar iterator that returns the groups based on a filter. Any ideas?

Upvotes: 1

Views: 409

Answers (4)

Onyambu
Onyambu

Reputation: 79288

You could also use reduce from functools:

from functools import reduce
def fun(x, y):
    if isinstance(x[-1], str) and x[-1].isdigit():
        x[-1] = x[-1] + y if y.isdigit()  else int(x[-1])
    else:
        x += [y]      
    return x

reduce(fun, '[10,[20,30]]', [''])[1:]
Out[]: ['[', 10, '[', 20, 30, ']']

Another approach may be to use recursion:

def fun_recurse(y, x=None):     
    if x is None:
        x = ['']
    if len(y) == 1:
        return x[1:] + [y]
    if isinstance(x[-1], str) and x[-1].isdigit():
       x[-1] = x[-1] + y[0] if y[0].isdigit() else int(x[-1])
       return fun_recurse(y[1:], x)
    else:
        return fun_recurse(y[1:], x + [y[0]])

fun_recurse('[10,[20,30]]')
Out[]: ['[', 10, '[', 20, 30, ']']

Upvotes: 0

Alain T.
Alain T.

Reputation: 42133

Use str.isdigit as your grouping key and convert groups that have a key of True to integers:

from itertools import groupby

s = "[10,20]"

r = [[str,int][k]("".join(g)) for k,g in groupby(s,key=str.isdigit)]

print(r)
['[', 10, ',', 20, ']']

Upvotes: 1

OysterShucker
OysterShucker

Reputation: 5531

This can also be done with regex with ease.

import re

NUMCHR = re.compile(r'\d+|[^\d]') #consecutive digits OR one "not-a-digit"
data   = '[10,20]'
out    = [int(m[0]) if m[0].isdigit() else m[0] for m in NUMCHR.finditer(data)]

print(out) #['[', 10, ',', 20, ']']

.finditer (in this case) will return either consecutive numbers or only 1 character on each iteration. We just check the return and proceed accordingly.

Upvotes: 1

pho
pho

Reputation: 25489

Group by whether this character is numeric or not. This can be done using the str.isnumeric function as the key argument to groupby().

s = "[10,20]"
g = itertools.groupby(s, key=str.isnumeric)

Then, for the True groups, convert it to an integer. Leave False groups as-is. Since the values of the groupby are iterators where each element is a separate character, you need to join it with "" to convert it into a single string, and optionally convert that string to an integer.

lst = [int("".join(chars)) if is_numeric else "".join(chars) for is_numeric, chars in g]

Which gives:

['[', 10, ',', 20, ']']

In one line:

lst = [                  int("".join(chars)) 
      if is_numeric else "".join(chars) 
      for is_numeric, chars in itertools.groupby(s, key=str.isnumeric)
      ]

Upvotes: 2

Related Questions