1

I am trying to create a simple function to reformat items in a list then print them

This is simple enough for a list:

my_list = [1/3,7/11,2/5] #generate list of floats
formatted_list = ['%.2f' % elem for elem in my_list] #format to 2 d.p
print(formatted_list) #print re-formatted list

But I have a list of nested lists. To loop through each item of these I need to do something like in this answer. That then allows me to write say

my_list = [(1/3,1/4,1/5), (2/3,2/4,2/5), 3/3]
formatted_list = ['%.2f' % elem for elem in traverse(my_list)] #loop over unpacked list

However the major drawback of this, is that it no longer preserves the structure (the tuples within the list).

That is:

print(formatted_list)

returns

0.33, 0.25, 0.2, 0.66, 0.5, 0.4, 1.0
#I want (0.33, 0.25, 0.2), (0.66, 0.5, 0.4), 1.0

Is there a way of reformatting, or more generally iterating over items in an arbitrarily deep nested list, to return a list with the same structure as the original?

Greedo
  • 4,967
  • 2
  • 30
  • 78

2 Answers2

5

This one supports arbitrary nesting of lists/tuples/sets of floats.

def myformat(x):
    return '%.2f' % x if isinstance(x, float) else type(x)(map(myformat, x))

Demo:

>>> myformat([(1/3, 1/4), [2/3, (2/4, {2/5})], 3/3])
[('0.33', '0.25'), ['0.67', ('0.50', {'0.40'})], '1.00']

Alternative implementation that also works for other types of numbers:

def myformat(x):
    try:
        return type(x)(map(myformat, x))
    except:
        return '%.2f' % x

Alternatively, the original version could use isinstance(x, numbers.Number).

Stefan Pochmann
  • 27,593
  • 8
  • 44
  • 107
  • oh maps. I love them – Bharath M Shetty Oct 28 '17 at 16:25
  • 1
    @Bharathshetty Me, too, although here I'm more happy with the `type(x)(...)` construct. Haven't seen that before. – Stefan Pochmann Oct 28 '17 at 16:27
  • This is great, would you be able to provide a brief explanation of what's going on? I am relatively new to python and haven't come across this construct before – Greedo Oct 28 '17 at 16:28
  • @Greedo I just check whether it's a float, and if so, return it formatted. Otherwise, apply my function recursively to all elements and return the results in the original container type. – Stefan Pochmann Oct 28 '17 at 16:30
  • @StefanPochmann I don't know, but I just copied your code in the terminal. While running, it gives me an error.`TypeError: argument 2 to map() must support iteration` – Miraj50 Oct 28 '17 at 16:37
  • @StefanPochmann I have just copied everything (Ctrl+C and Ctrl+Shift+V). Also tried it on an [online compiler](https://www.tutorialspoint.com/execute_python_online.php).Getting the same results. – Miraj50 Oct 28 '17 at 16:41
  • 1
    @Miraj50 Ah, you're probably using Python 2. So you have ints instead of floats. And then of course it goes wrong. The question is about Python 3 and floats. – Stefan Pochmann Oct 28 '17 at 16:42
  • 1
    @Miraj50 In Python 2 you could replace my `float` with `(float, int)` or `numbers.Number` (after importing `numbers`). Or reverse the logic and test for tuple/list. Or use "try recursion except format". – Stefan Pochmann Oct 28 '17 at 16:49
  • @Miraj50 Thanks for pointing it out. I added my try-except version now. – Stefan Pochmann Oct 28 '17 at 16:57
1

Maybe a nested comprehension help i.e

formatted =  [tuple(['%.2f' % j for j in elem]) if isinstance(elem,tuple) else '%.2f' % elem for elem in my_list ]
Bharath M Shetty
  • 30,075
  • 6
  • 57
  • 108
  • 1
    Thanks, this works perfectly for the example I gave. However I believe it only works for single depth nesting `[1/3,(1/4,1/5)]`, and not for arbitrary depth: `[((1/2,1/3),1/4),1/5]` should give `[((0.5,0.33),0.25),0.5]`, but instead gives an error. Do you know how I might fix this to work with arbitrary nesting? – Greedo Oct 28 '17 at 16:23
  • I would suggest @Stefan Pochmann's solution. Here mapping is the way to go in case of arbitraty nesting. – Bharath M Shetty Oct 28 '17 at 16:26