Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Format integer in Lua

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.

like image 313
Hultin Avatar asked Jun 12 '12 02:06

Hultin


3 Answers

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!')
like image 173
Bart Kiers Avatar answered Oct 09 '22 19:10

Bart Kiers


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
like image 31
voithos Avatar answered Oct 09 '22 17:10

voithos


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.

like image 39
Paul Kulchenko Avatar answered Oct 09 '22 18:10

Paul Kulchenko