asdfgh
asdfgh

Reputation: 1

Changing types within lists of lists (Python)

so I have a function which takes a list of lists and changes the type of each value depending on what the value represents:

def change_list(x):
    """
    Convert every str in x to an int if it represents a
    integer, a float if it represents a decimal number, a bool if it is 
    True/False, and None if it is either 'null' or an empty str

    >>> x = [['xy3'], ['-456'], ['True', '4.5']]
    >>> change_list(x)
    >>> x
    [['xy3' , -456], [True], [4.5]]
    """
    for ch in x:
        for c in ch:
            if c.isdigit() == True:
                c = int(c)

I've only posted part of the code, I feel as though once I can get that sorted I can apply a similar method in other if/elif/else to be able to get it all straight. My problem is when I apply this sort of method and then call x again the list is still returned as strings instead of ints or floats or bools.

ie if I called x after executing this function I would get:

x = [['xy3'], ['-456'], ['True', '4.5']]

instead of what is in the example code in the function. I'm not sure what's going wrong, any advice would be helpful.

Upvotes: 0

Views: 535

Answers (6)

Aaditya Ura
Aaditya Ura

Reputation: 12669

Because when you do :

for ch in x:
    for c in ch:
        if c.isdigit() == True:
            c = int(c)    #yes it changed the type but it doesn't stroed in list 

Yes you are changing the type but where are you storing changed stuff ??

For that, you have to tell the list to change at that index and for that, you can use enumerate :

item[index]=int(item1)

Second thing you are using isdigit() on float which will not work :

str.isdigit() will only return true if all characters in the string are digits. . and - are punctuation, not a digit.

So you can use try these two methods :

First Method :

x = [['xy3'], ['-456'], ['True', '4.5']]
for item in x:
    if isinstance(item,list):
        for index,item1 in enumerate(item):
            if item1.replace("-","").isdigit():
                item[index]=int(item1)
            elif item1.replace(".","").isdigit():
                item[index]=float(item1)

print(x)

output:

[['xy3'], [-456], ['True', 4.5]]

or if you want you can convert all int to float :

x = [['xy3'], ['-456'], ['True', '4.5']]
for item in x:
    if isinstance(item,list):
        for index,item1 in enumerate(item):
            if item1.replace("-","").replace(".","").isdigit():
                item[index]=float(item1)

print(x)

Second Method:

You can define your own isdigit() function :

x = [['xy3'], ['-456'], ['True', '4.5']]
def isdigit(x):
    try:
        float(x)
        return True
    except ValueError:
        pass

Then one line solution :

print([[float(item1) if '.' in item1 else int(item1)] if isdigit(item1)  else item1 for item in x if isinstance(item,list) for index,item1 in enumerate(item)])

Detailed Solution:

for item in x:
    if isinstance(item,list):
        for index,item1 in enumerate(item):
            if isdigit(item1)==True:
                if '.' in item1:
                    item[index]=float(item1)
                else:
                    item[index]=int(item1)

print(x)

output:

[['xy3'], [-456], ['True', 4.5]]

Upvotes: 1

Rajan
Rajan

Reputation: 1497

First: isdigit return false for negative value, that's whay you are not getting the integer in X

>>> x[1][0].isdigit()
False

Second: You are not replacing the updating the variable x in c = int(c)

Working Example will be like:

x = [['xy3'], ['-456'], ['True', '4.5']]
for index, value in enumerate(x):
     for i,v in enumerate(value):
         try:
             x[index][i] = eval(v)
         except:
             pass

Upvotes: 0

Nir Alfasi
Nir Alfasi

Reputation: 53525

isdigit and isnumeric will not work since '-456' contains - and '4.5' contains '.'

Instead do:

x = [['xy3'], ['-456'], ['True', '4.5'], ['3']]
for ch in x:
    for i in range(len(ch)):
        try:
            ch[i] = float(ch[i])
            if int(ch[i]) == ch[i]:
                ch[i] = int(ch[i])
        except:
            if ch[i] in ['True', 'False']:
                ch[i] = (['True', 'False'][0] == ch[i]) 
print(x)   

OUTPUT

[['xy3'], [-456], [True, 4.5]]

Upvotes: 0

Abel Riboulot
Abel Riboulot

Reputation: 178

Currently you are using the value of of the list, but not updating it. You therefore need to enumerate it, and change the list element directly by referencing to it.

The correct code would look something like this:

for idx,ch in enumerate(x):
    for idx2,c in enumerate(ch):
        if c.isdigit() == True:
            x[idx][idx2] = int(c)

Upvotes: 0

Amit Joki
Amit Joki

Reputation: 59232

You're not updating the list. You're just assigning the value another value, which does, umm..nothing. Use an enumerate function and the index value it provides and then change the value using the index.

for ch in x:
    for c in ch:
        if c.isdigit() == True:
            c = int(c) # You're doing 'xyz' = int('xyz') which does nothing

Better still, since you're wanting to generate a new list based on the current list, it would be better to go for a map

inp_list = [...] # Your list
out_list = list(map(lambda nums: int(n) for n in nums if n.isDigit(), inp_list))
# The above is for only integer conversion but you get the idea. 

Upvotes: 0

Julien
Julien

Reputation: 15071

you need to change the list element itself, not the local reference c or ch:

for i,ch in enumerate(x):
    if ch ... # whatever logic
        x[i] = ... # whatever value

Upvotes: 0

Related Questions