10

I'd like to format a number to look like 1,234 or 1,234,432 or 123,456,789, you get the idea. I tried doing this as follows:

function reformatint(i)
    local length = string.len(i)
    for v = 1, math.floor(length/3) do
        for k = 1, 3 do
            newint = string.sub(mystring, -k*v)
        end
        newint = ','..newint
    end
    return newint
end

As you can see, a failed attempt, my problem is that I can't figure out what the error is because the program I am running this in refuses to report an error back to me.

4 Answers 4

18

Here's a function that takes negative numbers, and fractional parts into account:

function format_int(number)

  local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')

  -- reverse the int-string and append a comma to all blocks of 3 digits
  int = int:reverse():gsub("(%d%d%d)", "%1,")

  -- reverse the int-string back remove an optional comma and put the 
  -- optional minus and fractional part back
  return minus .. int:reverse():gsub("^,", "") .. fraction
end

assert(format_int(1234)              == '1,234')
assert(format_int(1234567)           == '1,234,567')
assert(format_int(123456789)         == '123,456,789')
assert(format_int(123456789.1234)    == '123,456,789.1234')
assert(format_int(-123456789.)       == '-123,456,789')
assert(format_int(-123456789.1234)   == '-123,456,789.1234')
assert(format_int('-123456789.1234') == '-123,456,789.1234')

print('All tests passed!')
Sign up to request clarification or add additional context in comments.

If you include decimals (not asked for), why didn't you include comma-separation for them?
I found this when looking for fix for string.format("%.1f", mynumber) rounding numbers - I've resolved it by string match - tostring(mynumber):match("^%d+%.%d") (not rounding was more preferred in my case)
7

Well, let's take this from the top down. First of all, it's failing because you've got a reference error:

    ...
        for k = 1, 3 do
            newint = string.sub(mystring, -k*v) -- What is 'mystring'?
        end
    ...

Most likely you want i to be there, not mystring.

Second, while replacing mystring with i will fix the errors, it still won't work correctly.

> =reformatint(100)
,100
> =reformatint(1)
,000

That's obviously not right. It seems like what you're trying to do is go through the string, and build up the new string with the commas added. But there are a couple of problems...

function reformatint(i)
    local length = string.len(i)
    for v = 1, math.floor(length/3) do
        for k = 1, 3 do -- What is this inner loop for?
            newint = string.sub(mystring, -k*v) -- This chops off the end of
                                                -- your string only
        end
        newint = ','..newint -- This will make your result have a ',' at
                             -- the beginning, no matter what
    end
    return newint
end

With some rework, you can get a function that work.

function reformatint(integer)
    for i = 1, math.floor((string.len(integer)-1) / 3) do
        integer = string.sub(integer, 1, -3*i-i) ..
                  ',' ..
                  string.sub(integer, -3*i-i+1)
    end
    return integer
end

The function above seems to work correctly. However, it's fairly convoluted... Might want to make it more readable.

As a side note, a quick google search finds a function that has already been made for this:

function comma_value(amount)
  local formatted = amount
  while true do  
    formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
    if (k==0) then
      break
    end
  end
  return formatted
end

uff, I ripped the bit containing mystring from a webpage, obviously I was silly enough to forget to swap that out. as it turns out, my google searches was terribly incorrect. I would like to thank you, and I will look into much deeper how string.sub() operates. I hope that my stupidity thus far will atleast let me learn something.
@Hultin: No need to look down on yourself - we all make mistakes, and they are sometimes silly ones. That is how we learn. And yes, understanding string.sub() is a worthwhile endeavor - it is actually slightly different than substring functions in other languages. Also, welcome to StackOverflow!
5

You can do without loops:

function numWithCommas(n)
  return tostring(math.floor(n)):reverse():gsub("(%d%d%d)","%1,")
                                :gsub(",(%-?)$","%1"):reverse()
end

assert(numWithCommas(100000) == "100,000")
assert(numWithCommas(100) == "100")
assert(numWithCommas(-100000) == "-100,000")
assert(numWithCommas(10000000) == "10,000,000")
assert(numWithCommas(10000000.00) == "10,000,000")

The second gsub is needed to avoid -,100 being generated.

Comments

1

I remember discussing about this in the LÖVE forums ... let me look for it...

Found it!

This will work with positive integers:

function reformatInt(i)
  return tostring(i):reverse():gsub("%d%d%d", "%1,"):reverse():gsub("^,", "")
end

On the link above you may read details about implementation.

Comments

Your Answer

Draft saved
Draft discarded

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.