# Coding Challenge 2020-05-19: Number formatting and abbreviation

This is a coding challenge inspired bt @starmaq’s Coding Challenge
The reason it’s here it’s because

Here’s the reqiurement:

• Has to work on Roblox Studio so it has to be written in Lua 5.1 and you can’t use features vanilla Lua has but Roblox Lua disabled
• No HttpService

You are given a data to create a function that formats and abbreviates the number using that data. The data is a table

``````{
decimalSymbol = ',',
groupSymbol = ' ',
negativeSymbol = '-',
groupSize = { 3 },
minimumGroupingDigits = 2,
compact = { '0', '0', '0', '0', '0', '0', '0 M', '00 M', '000 M', '0 B', '00 B', '000 B', '0 T', '00 T', '000 T' }
}
``````
key Meaning
decimalSymbol The decimal symbol
groupSymbol The digit grouping symbol
negativeSymbol The negative sign symbol on negative numbers
groupSize The size of grouping, `{ 3 }` means group every digit `{ 3, 2 }` means group the last 3 then every 2 digits (e.g. 12,34,56,789).

minimumGroupingDigits:
A value that controls what number can be grouped, it cannot be `0`, it’s this is infinity, don’t group the number at all.

minimumGroupingDigits index 1 of grouping size value output
1 3 1000 1,000
2 3 1000 1000
1 3 10000 10,000
2 3 10000 10,000
3 3 10000 10000
3 3 100000 100,000
1 4 10000 1,0000
1 4 100000 10,0000
2 4 10000 10000
2 4 100000 10,0000
inf 3 1234567890 1234567890

compact:
The table of abbreviated string

• The minimumGroupingDigits is always 2 if the minimumGroupingDigit normally is 1 if you’re abbreviating numbers, no exceptions
• Just `0` means don’t abbreviate the number
• The amount of `0` is the size of the group (e.g. `12345` on `00K` will be `12K`)
• Ignore the single quote, e.g. `12345` on `00K'.'` will be `12K.`, but `''` will be `'`
• Group the abbreviated number too e.g. (`12,345T` instead of `12345T`)
• The decimal size must be zero if the size is bigger than 1 (e.g. `12K` instead of `12.3K`), and the decimal size must be one if the size is 1 and it’s not zero (e.g. `1.2K` instead of `1K` but `1K` instead of `1.0K`)

There are no holding hands, I can only help if you don’t get it or the rules.
Boilerplate:

``````local data =
{
{
decimalSymbol = ',',
groupSymbol = ' ',
negativeSymbol = '-',
groupSize = { 3 },
minimumGroupingDigits = 2,
compact = { '0', '0', '0', '0', '0', '0', '0 M', '00 M', '000 M', '0 B', '00 B', '000 B', '0 T', '00 T', '000 T' }
},
{
decimalSymbol = ' decimal ',
groupSymbol = ' group ',
negativeSymbol = '-',
groupSize = { 3, 2 },
minimumGroupingDigits = 1,
compact = { '0', '0', '0', '0', 'TestA 0', 'TestA 00', 'TestB 000', '0 TestC', 'TestC 00 ttest', '000 TestC' }
},
{
decimalSymbol = '.',
groupSymbol = ',',
negativeSymbol = '-',
groupSize = { 3 },
minimumGroupingDigits = 1,
compact = { '0', '0', '0', '0', '0万', '00万', '000万', '0000万', '0億', '00億', '000億', '0000億', '0兆', '00兆', '000兆' }
},
{
decimalSymbol = ',',
groupSymbol = '.',
negativeSymbol = '-',
groupSize = { 3 },
minimumGroupingDigits = 1,
compact = { '0', '0', '0', '0', '0', '0', '0 Mio.', '0 Mio.', '0 Mio.', '0 Mrd.', '00 Mrd.', '000 Mrd.', '0 Bio.', '00 Bio.', '000 Bio.' }
},
{
decimalSymbol = ',',
groupSymbol = '.',
negativeSymbol = '-',
groupSize = { 3 },
minimumGroupingDigits = 2,
compact = { '0', '0', '0', '0', '0', '0', '0 M', '00 M', '000 M', '0000 M', '00 MRD', '000 MRD', '0 B', '00 B', '000 B' }
},
{
decimalSymbol = '.',
groupSymbol = ',',
negativeSymbol = '-',
groupSize = { 3 },
minimumGroupingDigits = 1,
compact = { '0', '0', '0', '0K', '00K', '000K', '0M', '00M', '000M', '0B', '00B', '000B', '0T', '00T', '000T' };
}
}
local examples = { 1500, 23456, 5343442, 865849384, 12345000000000000 }
function FormatNumber(value, data)
end;

function AbbreviateNumber(value, data)
end;

for _, d in ipairs(data) do
for _, v in ipairs(examples) do
print(FormatNumber(v, d))
print(AbbreviateNumber(v, d))
end
end
``````

Expected output
Data 1:

1500
1500
23 456
23 456
5 343 442
5,3 M
865 849 384
865 M
12 345 000 000 000 000
12 345 T

Data 2:

1 group 500
1500
23 group 456
TestA 2 decimal 3
53 group 43 group 442
TestB 534
86 group 58 group 49 group 384
TestC 86 ttest
12 group 34 group 50 group 00 group 00 group 00 group 00 group 000
12 group 34 group 50 group 00 group 000 TestC

Data 3:

1,500
1500
23,456
2.3万
5,343,442
543万
865,849,384
8.6億
12,345,000,000,000,000
12,345兆

Data 4:

1.500
1500
23.456
23.456
5.343.442
5,3 Mio.
865.849.384
865 Mio.
12.345.000.000.000.000
12.345 Bio.

Data 5:

1500
1500
23.456
23.456
5.343.442
5,3 M
865.849.384
865 M
12.345.000.000.000.000
12.345 B

Data 6:

1,500
1.5K
23K
23K
5,343,442
5.3M
865,849,384
865M
12,345,000,000,000,000
12,345T

Good luck!

1> Do the formatting number first. Convert number to string first, use `:format` instead of `tostring`, as because tostring will also include expoentents, which we don’t want here.
2> Create the parts, `negative, integer, decimal, fraction`
3> Format the integer
4> The return the value, replace `-` with the negative symbol and `.` (doesn’t matter literal dot or not because `.` means any charcter, and the decimal can only be `` or `.`) to a decimal symbol to ensure it’s actually there, and not empty
5> Create minimumGroupingDigit override, just in case

``````function FormatNumber(value, data, mgd_override)
if type(value) == "number" then
value = ("%.0f"):format(value);
end;
local neg, int, dec, frac = value:match("(-?)(%d*)([.]?)(%d*)");
if #int >= data.groupSize[1] + (mgd_override or data.minimumGroupingDigits) then
-- This is the grouping part
local ret = '';
local i = 1;
while #int > data.groupSize[i] do
ret = data.groupSymbol .. value:sub(-data.groupSize[i]) .. ret;
int = int:sub(1, -(data.groupSize[i] + 1));
if i < #data.groupSize then
i = i + 1
end;
end;
int = int .. ret;
end;
return neg:gsub('-', data.negativeSymbol) .. int .. dec:gsub('.', data.decimalSymbol) .. frac;
end;
``````

5> Once your done, abbreviate the number, check if the value is 0 because the minimumGroupingDigits must be ≥2, use math.max to get the highest value
6> FormatNumber with minimumGroupingDigit overrided to ≥2
7> Use string.sub/divide numbers to get parts of the number.
8> Use string:gsub() to find how many zeros are there, do note that subtract length of value to length of the data.compact size with max at 0 just in case there are more
9> Add a full stop (decimal separator) with the next part of the number if the size is 1

``````function AbbreviateNumber(value, data)
if type(value) == "number" then
value = ("%.0f"):format(value);
end;
local cformat = data.compact[math.min(#value, #data.compact)];
if cformat == '0' or cformat == '' then
return FormatNumber(value, data, math.max(data.minimumGroupingDigits, 2));
else
local _, csize = cformat:gsub('0', '');
csize = csize + math.max(#value - #data.compact, 0);
local dsize = csize == 1 and 1 or 0;
return (cformat:gsub(
'0+',
FormatNumber(value:sub(1, csize) .. (dsize == 0 and '' or '.' .. value:sub(csize + 1, csize + dsize)), data, math.max(data.minimumGroupingDigits, 2))
):gsub("'([^']+)'", "%1"));
end;
end;
``````
8 Likes

Since this is a tutorial you are required to provide the answer upon posting the topic. You can see other coding challenge topics for examples of the right way to do this. You can just hide the answer under a Details tag.