1

I have a list as below:

    input_a= [['a','12','','23.5'],[12.3,'b2','-23.4',-32],[-25.4,'c']]

I want to convert the numbers in this to numbers to get an output like this

    output_a = [['a',12,'',23.5],[12.3,'b2',-23.4,-32],[-25.4,'c']]

I wrote the following code to get this to work:

    def str_to_num(str_object=None):
      if not isinstance(str_object,str):
        return str_object
      try:
        x = int(str_object)
      except ValueError:
        try:
           x = float(str_object)
        except ValueError:
           x =str_object
      return x

    def getNumbers(num_object=None,return_container=None):
      a = return_container
      if isinstance(num_object,list):
        b = []
        for list_element in num_object:
           if isinstance(list_element,list):
              x = getNumbers(list_element,a)
           if isinstance(list_element,str):
              y = str_to_num(list_element)
              b += [y]
           if isinstance(list_element,int):
              y = list_element
              b += [y]
           if isinstance(list_element,float):
              y = list_element
              b += [y]
        a += [b]
      return return_container

    return_container = []
    output_a = getNumbers(input_a,return_container)[:-1]

This works (for this situation). But I have two problems: 1. It does not work so well if there is another level of nesting of list. I want to make it such that it can handle any level of nesting. so if

    input_b= [['a','12','','23.5',['15']],[12.3,'b2','-23.4',-32],[-25.4,'c']]

This gives

    output_b= [[-15],['a',12,'',23.5],[12.3,'b2',-23.4,-32],[-25.4,'c']]

which is wrong as the [-15] should be nested within the first sub-list.

  1. The code is very verbose!! I am sure there must be a much simpler way to handle this.
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
Alhpa Delta
  • 3,385
  • 4
  • 16
  • 31

1 Answers1

2

You follow the tradition of "Ask forgiveness not permission" - explain and simply try to convert.

input_b= [['a','12','','23.5',['15']],[12.3,'b2','-23.4',-32],[-25.4,'c']]

def parseEm(l):
    """Parses a list of mixed strings, strings of floats, strings of ints, ints and floats.
    Returns int where possible, float where possible else string as list elements."""
    def tryParse(elem):
        def asInt(e):
            """Tries to convert to int, else returns None"""
            try:
                return int(e)
            except:
                return None
        def asFloat(e):
            """Tries to convert to float, else returns None"""
            try:
                return float(e)
            except:
                return None        
        # if elem itself is a list, use list comp to get down it's elements
        if isinstance(elem,list):
            return [tryParse(q) for q in elem]

        # try to convert to int, else to float or return original value
        if isinstance(elem,str):
            a,b = asInt(elem),asFloat(elem)
            if a is not None:
                return a
            elif b is not None:
                return b

        return elem

        # this does not work, as interger 0 is considered false: 
        # return asInt(elem) or asFloat(elem) or elem

    # apply tryParse to all elements of the input list
    return [tryParse(k) for k in l]

print(parseEm(input_b))

Output:

[['a', 12, '', 23.5, [15]], [12, 'b2', -23.4, -32], [-25, 'c']]

Be careful though, some things can be converted to floats that you might (not) want to - f.e. ["NaN"] is a valid list with 1 float in it.

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • This is lovely!! I checked it for more nesting and it works. With one issue. With a negative float number it converts it into int. so,for example -25.4 in the last sub-list is getting coverted to -25. – Alhpa Delta Aug 30 '18 at 13:17
  • 1
    @AlhpaDelta I changed the code to only parse strings through isFloat and isInt - should work now – Patrick Artner Aug 30 '18 at 13:36
  • There is a situation in which it is failing though. If a number has a comma. Like '1,222'. How do we get around that? – Alhpa Delta Aug 30 '18 at 14:20
  • You dont. It is not a valid interger or float representation of a python number. It is a human readable format using a thousands-divider character that depends on the culture you live it. For me its `'.'` to seperate thousands and `','` for partials: `'1.234.567,89 €`` . If you know for sure you will only ever get `,` as thousands-divider, you can change `asFloat(e)` to use `return float(e.replace(",",""))` – Patrick Artner Aug 30 '18 at 14:23