4

I've been searching for a simpler way to do this, but i'm not sure what search parameters to use. I have a floating point number, that i would like to round, convert to a string, then specify a custom format on the string. I've read through the .format docs, but can't see if it's possible to do this using normal string formatting.

The output i want is just a normal string, with spaces every three chars, except for the last ones, which should have a space four chars before the end.

For example, i made this convoluted function that does what i want in an inefficient way:

def my_formatter(value): final = [] # round float and convert to list of strings of individual chars c = [i for i in '{:.0f}'.format(value)] if len(c) > 3: final.append(''.join(c[-4:])) c = c[:-4] else: return ''.join(c) for i in range(0, len(c) // 3 + 1, 1): if len(c) > 2: final.insert(0, ''.join(c[-3:])) c = c[:-3] elif len(c) > 0: final.insert(0, ''.join(c)) return(' '.join(final)) 

e.g.

>>> my_formatter(123456789.12) >>> '12 345 6789' >>> my_formatter(12345678912.34) >>> '1 234 567 8912' 

Would really appreciate guidance on doing this in a simpler / more efficient way.

12
  • 1
    What's the format of the string you want as an output? Commented Mar 14, 2018 at 4:33
  • Just want a normal string, with spaces every three chars, except for the last ones, which should have a space four chars before the end. Commented Mar 14, 2018 at 4:35
  • 2
    Are you trying to do this to match Indian-style monetary/large-number grouping? If so, there's a recent discussion on either python-ideas or python-dev to add either that, or a more general way to specify digit groupings that would be able to handle that, to Python 3.7 or 3.8. I don't know the status of the proposal, and obviously you can't just wait around for 3.8 and hope the feature is there, but… I'll bet the discussion had multiple people saying "This is unnecessary, because any idiot could just write this trivial code" (followed by code that does exactly what you want). Commented Mar 14, 2018 at 4:48
  • 1
    @KeyurPotdar It'll be in the python-ideas archive. If you prefer searching via NNTP or one of the nicer third-party archives, I think you can find info about them somewhere in the Community section, maybe here. The discussion started somewhere around December and January, and the thread title was… I think the Hindi work for 10000 or something like that, but I don't remember for sure. Commented Mar 14, 2018 at 5:10
  • 1
    @KeyurPotdar Oops; thanks. Anyway, any code that did 2-2-2-…-3 could be adapted pretty quickly to 2-2-2-…-4. Also, I found the initial post, and you can click Next By Thread from there. Commented Mar 14, 2018 at 5:15

5 Answers 5

3

Took a slightly different angle but this uses a third party function partition_all. In short, I use it to group the string into groups of 3 plus the final group if there are less than 3 chars. You may prefer this as there are no for loops or conditionals but it's basically cosmetic differences.

from toolz.itertoolz import partition_all def simpleformat(x): x = str(round(x)) a, b = x[:-4], x[-4:] strings = [''.join(x[::-1]) for x in reversed(list(partition_all(3, a[::-1])))] return ' '.join(strings + [b]) 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for sharing!
3

Try this:

def my_formatter(x): # round it as text txt = "{:.0f}".format(x) # find split indices splits = [None] + list(range(-4, -len(txt), -3)) + [None] # slice and rejoin return " ".join( reversed([txt[i:j] for i, j in zip(splits[1:], splits[:-1])])) 

Then

>>> my_formatter(123456789.1) 12 345 6789 >>> my_formatter(1123456789.1) 112 345 6789 >>> my_formatter(11123456789.1) 1 112 345 6789 

1 Comment

Nice; doing the backward range and one reverse instead of two reverses.
2

Here is a pretty simple solution using a loop over the elements in the reverse order such that counting the indices is easier:

num = 12345678912.34 temp = [] for ix, c in enumerate(reversed(str(round(num)))): if ix%3 == 0 and ix !=0: temp.extend([c, ' ']) else: temp.extend(c) ''.join(list(reversed(temp))) 

Output:

'1 234 567 8912'

Using list comprehensions we can do this in a single very confusing line as

num = 12345678912.34 ''.join(list(reversed(list(''.join([c+' ' if(ix%3 == 0 and ix!=0) else c for ix, c in enumerate(reversed(str(round(num))))]))))) 

'1 234 567 8912'

7 Comments

I'll simplify it with list comprehensions.
if ix==3 and ix%3==0? If the first part is true, the second part is automatically true as well.
Also, you might want make temp a list of strings so you can just temp.append each time and then ''.join(temp) at the end, to avoid all the string concatenation. Not really any more complicated, and more idiomatic, and a lot more efficient for older versions of Python.
@JahKnows Nice approach, thanks for sharing! Note that for 1234567890 it appends a space at the front, but in my current state i've got a good mind to just .strip() the return value.
@abarnert, I tried your suggestion regarding the x==3 and x%3 however it is necessary, because we only want to do the first grouping of 4 for the first group. Note that ix=3 is the 4th character.
|
2

Another approch is to use locale if available on your system of course, and use format.

import locale for v in ('fr_FR.UTF-8', 'en_GB.UTF-8'): locale.setlocale(locale.LC_NUMERIC, v) print(v, '>> {:n}'.format(111222333999)) 

Comments

1

May as well share another slightly different variant, but still can't shake the feeling that there's some sublime way that we just can't see. Haven't marked any answers as correct yet, because i'm convinced python can do this in a simpler way, somehow. What's also driving me crazy is that if i remember correctly VB's format command can handle this (with a pattern like "### ####0"). Maybe it's just a case of not understanding how to use Python's .format correctly.

The below accepts a float or decimal and a list indicating split positions. If there are still digits in the string after consuming the last split position, it re-applies that until it reaches the start of the string.

def format_number(num, pat, sep=' '): fmt = [] strn = "{:.0f}".format(num) while strn: p = pat.pop() if pat else p fmt.append(strn[-p:]) strn = strn[:-p] if len(strn) > p else '' return sep.join(fmt[::-1]) >>> format_number(123456789, [3, 4]) >>> '12 345 6789' >>> format_number(1234567890, [3]) >>> '1 234 567 890' 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.