0

I have some lists of lists. The length and depth of each list varies in an unpredictable way. I want to apply a function to each of the bottom elements. For purposes of this question, we can say I just need to convert each int to a float.

I searched the site and found this: Python: How to loop a list of lists of varying depth?

However, when adapting this to my problem, it successfully accesses each element, but does not seem to transform the actual values. At the end, I still have a list of int's, when I want a list of floats.

def list_flatten(my_list):
    for item in my_list:
        if(isinstance(item,list)):
            list_flatten(item)
        else:
            item = float(item)
    return my_list

problem = [[[[1,1],[2,2],[3,3]],[[4,4],[5,5],[6,6]],[[7,7],[[8,8],[9,9]]]]]
print(list_flatten(problem))
Geoff
  • 35
  • 3
  • 1
    Could you please show us the expected output? Do you just want this: `[[1,2], [3,6], [9,8]]` to become `[1.0, 2.0, 3.0, 6.0, 9.0, 8.0]` ? – PL200 Oct 29 '18 at 01:15
  • You are not saving the float results anywhere – Sheldore Oct 29 '18 at 01:16
  • You gotta understand mutable and immutable data types if you want to become better at python – pyeR_biz Oct 29 '18 at 01:22
  • Thanks, all. I thought item = float(item) was saving the results, but perhaps that's not mutable, as other commentator calls out. – Geoff Oct 29 '18 at 03:34

4 Answers4

1

That is because, in the following:

for item in some_list:
    item = some_function(item)

.. you are merely re-assigning item to a new value. You aren't changing the actual element in the list.

Another problem in your code is that you're not doing anything with the returned result from the list_flatten(..) recursive call. It will not magically be fit inside the resultant array -- you'll need to do it using perhaps += operator on the list object.

This is the working version of your code:

def list_flatten(my_list):
    res = []
    for item in my_list:
        if isinstance(item,list):
            res += list_flatten(item)
        else:
            res.append(float(item))
    return res
UltraInstinct
  • 43,308
  • 12
  • 81
  • 104
1

You were almost there, but the original list items were never converted. Consider this:

def list_flatten(my_list_or_value):
    if isinstance(my_list_or_value,list):
        return [list_flatten(sublist) for sublist in my_list_or_value]
    return float(my_list_or_value)

problem = [[[[1,1],[2,2],[3,3]],[[4,4],[5,5],[6,6]],[[7,7],[[8,8],[9,9]]]]]
print(list_flatten(problem))

-- it iterates over any items or sub-items and only returns float(x) if you are at the deepest level.

Jongware
  • 22,200
  • 8
  • 54
  • 100
0

Here's a version that should work for any sequence type. It will return a new sequence with the same types as the old one:

from collections.abc import Sequence

def nested_convert(sequence, func):
    cls = type(sequence)
    return cls(nested_convert(subseq, func) if isinstance(subseq, Sequence) 
                                            else func(subseq) for subseq in sequence)

l = [((1, 2, 3), [5, 6, (2, 3)]), (3, 4, 5, [4, 5, 6])]
print(nested_convert(l, lambda x: x*2))
#   [((2, 4, 6), [10, 12, (4, 6)]), (6, 8, 10, [8, 10, 12])]
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
  • 1
    why `lambda x: x*2`? (*For purposes of this question, we can say I just need to convert each int to a float.*) – colidyre Oct 29 '18 at 01:27
  • Why not? It'll work with any single-argument callable: `nested_convert(l, float)` works just fine too. – Patrick Haugh Oct 29 '18 at 01:28
  • Thanks for response! I have to understand the more simple answers first, before trying to understand yours :) – Geoff Oct 29 '18 at 03:37
0

You've got the right idea for traversing down to the actual integer elements, but seem to be miss understanding how to modify them. If you're new to programming, welcome to your first lesson in "references" vs. "values."

Take a simple example:

arr = [1,2,3,4]
for el in arr:
    el = float(el)
    print el

print arr

# this will print
1.0
2.0
3.0
4.0
[1,2,3,4]

This is because when you iterate over the list, the variable el will take on the "value" of each element in arr rather than actually "referring" to the element itself -- which you can think of as the memory location the value resides in. Thus, your modifications to el are "working," but only on el -- the copy of the value in the array -- not the array itself.

If we change the code to this, you can modify the array itself as opposed to the local variable el:

arr = [1,2,3,4]
for i in range(len(arr)):
    arr[i] = float(arr[i])
    print arr[i]

print arr

# this will print
1.0
2.0
3.0
4.0
[1.0,2.0,3.0,4.0]

You can look up more about this concept by googling around for things like "pass by reference vs. pass by value" and "pointers." This can be a bit confusing, especially if you're only familiar with python and haven't seen/worked with C/C++ before -- languages which are very explicit about these differences and force you to learn about them early. Good luck :)

jlucier
  • 1,482
  • 10
  • 14