Search code examples
pythonlistpython-3.xnumbersalignment

Align numbers in sublist


I have a set of numbers that I want to align considering the comma:

   10    3          
  200    4000,222  3  1,5 
  200,21          0,3  2   
30000    4,5      1      

mylist = [['10', '3', '', ''], 
          ['200', '4000,222', '3', '1,5'], 
          ['200,21', '', '0,3', '2'], 
          ['30000', '4,5', '1', '']]

What I want is to align this list considering the comma:

expected result:

mylist = [['   10   ', '   3    ', '   ', '   '], 
          ['  200   ', '4000,222', '3  ', '1,5'], 
          ['  200,21', '        ', '0,3', '2  '], 
          ['30000   ', '   4,5  ', '1  ', '   ']]

I tried to turn the list:

mynewlist = list(zip(*mylist))  

and to find the longest part after the comma in every sublist:

for m in mynewlist:
    max([x[::-1].find(',') for x in m]

and to use rjust and ljust but I don't know how to ljust after a comma and rjust before the comma, both in the same string.

How can I resolve this without using format()? (I want to align with ljust and rjust)


Solution

  • Here's another approach that currently does the trick. Unfortunately, I can't see any simple way to make this work, maybe due to the time :-)

    Either way, I'll explain it. r is the result list created before hand.

    r = [[] for i in range(4)]
    

    Then we loop through the values and also grab an index with enumerate:

    for ind1, vals in enumerate(zip(*mylist)):
    

    Inside the loop we grab the max length of the decimal digits present and the max length of the word (the word w/o the decimal digits):

        l = max(len(v.partition(',')[2]) for v in vals) + 1
        mw = max(len(v if ',' not in v else v.split(',')[0]) for v in vals)
    

    Now we go through the values inside the tuple vals and build our results (yup, can't currently think of a way to avoid this nesting).

        for ind2, v in enumerate(vals):
    

    If it contains a comma, it should be formatted differently. Specifically, we rjust it based on the max length of a word mw and then add the decimal digits and any white-space needed:

            if ',' in v:
                n, d = v.split(',')
                v = "".join((n.rjust(mw),',', d, " " * (l - 1 - len(d))))
    

    In the opposite case, we simply .rjust and then add whitespace:

            else:
                v = "".join((v.rjust(mw) + " " * l))
    

    finally, we append to r.

            r[ind1].append(v)
    

    All together:

    r = [[] for i in range(4)]
    for ind1, vals in enumerate(zip(*mylist)):
        l = max(len(v.partition(',')[2]) for v in vals) + 1
        mw = max(len(v if ',' not in v else v.split(',')[0]) for v in vals)
        for ind2, v in enumerate(vals):
            if ',' in v:
                n, d = v.split(',')
                v = "".join((n.rjust(mw),',', d, " " * (l - 1 - len(d))))
            else:
                v = "".join((v.rjust(mw) + " " * l))
            r[ind1].append(v)
    

    Now, we can print it out:

    >>> print(*map(list,zip(*r)), sep='\n)
    ['   10   ', '   3    ', '   ', '   ']
    ['  200   ', '4000,222', '3  ', '1,5']
    ['  200,21', '        ', '0,3', '2  ']
    ['30000   ', '   4,5  ', '1  ', '   ']