19
\$\begingroup\$

Convert a large int (e. g. 1234567890) into a string containing the usual decimal prefixes k, M, G, T, etc. for kilo, mega, giga, tera, etc. The result shall be groups of three digits, interspersed by the prefixes (e. g. 1G234M567k890), so that it is easy to glance the order of magnitude of the number without counting the digits.

It shall work for numbers of all digit counts up to 15. Only non-negative numbers are allowed as input.

More test cases:

0 → 0
123 → 123
1234 → 1k234
1000 → 1k000
1000000000 → 1G000M000k000
\$\endgroup\$
9
  • 1
    \$\begingroup\$ you don't even need peta, because 15 digits is only up to tera. 123T 456G 789M 012k 345 \$\endgroup\$
    – Dorian
    Commented Oct 11, 2019 at 10:54
  • 2
    \$\begingroup\$ @GalenIvanov If the language cannot handle integers up to 15 digits, the input can be a string representation of the number in decimal format. \$\endgroup\$
    – Alfe
    Commented Oct 11, 2019 at 11:00
  • 4
    \$\begingroup\$ So a block of 0s should still display? E.g., 1000234 -> 1M000k234 \$\endgroup\$
    – Veskah
    Commented Oct 11, 2019 at 14:29
  • 4
    \$\begingroup\$ can we get more than 1 test case? \$\endgroup\$ Commented Oct 11, 2019 at 19:07
  • 4
    \$\begingroup\$ "easy to glance the order of magnitude". I personally find 1G000M000k000 much harder to parse than 1 000 000 000 \$\endgroup\$ Commented Oct 13, 2019 at 19:16

25 Answers 25

9
\$\begingroup\$

JavaScript (ES6), 64 bytes

Takes input as a string.

f=(n,i=0,[d,t]=n.split(/(...)$/))=>t?f(d,i+1)+['kMGT'[d&&i]]+t:d

Try it online!

How?

The regular expression /(...)/$ matches the last three digits of the integer, or nothing at all if there are less than 3 digits remaining.

Because there's a capturing group, these digits -- if present -- are included in the output of the .split() method.

Examples:

'123456789'.split(/(...)$/) // --> [ '123456', '789' '' ]
'123'.split(/(...)$/))      // --> [ '', '123' ]
'12'.split(/(...)$/))       // --> [ '12' ]
''.split(/(...)$/))         // --> [ '' ]

Commented

f = (                 // f is a recursive function taking:
  n,                  //   n = input
  i = 0,              //   i = index of next prefix
  [d, t] =            //   d = either the leading digits without the last 3 ones,
                      //       or all remaining digits if there are less than 3
    n.split(/(...)$/) //   t = last three digits (or undefined)
) =>                  //
  t ?                 // if t is defined:
    f(d, i + 1) +     //   append the result of a recursive call with d and i + 1
    ['kMGT'[d && i]]  //   append the prefix if d is defined, or an empty string otherwise
    + t               //   append the last 3 digits
  :                   // else:
    d                 //   stop recursion and return the remaining digits
\$\endgroup\$
2
  • \$\begingroup\$ 56 bytes: f=(n,i=0)=>n.replace(/.+(?=...)/g,d=>f(d,i+1)+'kMGT'[i]) \$\endgroup\$
    – tsh
    Commented Oct 15, 2019 at 8:19
  • \$\begingroup\$ @tsh I think this is different enough to be posted as a new answer. \$\endgroup\$
    – Arnauld
    Commented Oct 15, 2019 at 8:42
4
\$\begingroup\$

05AB1E, 15 14 bytes

R3ô"kMGTP"øSR¦

Try it online!

Thanks to Kevin Cruijssen for -1 byte

R       reverse input
3ô      split it into groups of 3 digits
"kMGTP" push the prefixes
ø       zip the groups of numbers with them
S       split to a list of characters
R       reverse again
¦       drop the first element
        implicitly print
\$\endgroup\$
3
  • 2
    \$\begingroup\$ You can change the JJ to S (which implicitly flattens) to output as a list of characters instead of a string, which is allowed when a string output is asked by default. (You could add a J in the footer if you want to pretty-print it.) \$\endgroup\$ Commented Oct 11, 2019 at 11:03
  • \$\begingroup\$ As you noticed above, P is not required. \$\endgroup\$
    – Arnauld
    Commented Oct 11, 2019 at 11:25
  • 1
    \$\begingroup\$ I know, but I can't remove it, because the code would cut off the number after 12 digits. It builds a list that looks like this: ["P1", "T234", "G567", "M890", "k123"]. Then it deletes the first letter. When I remove the "P", it can't fill the "P1" string. \$\endgroup\$
    – Dorian
    Commented Oct 11, 2019 at 11:41
4
\$\begingroup\$

Python 2, 57 bytes

f=lambda s,p="":s and f(s[:-3],p[1:]+"kMGT")+s[-3:]+p[:1]

Try it online!

Takes input as a string. If the output may have a trailing space, we can avoid some workarounds and save 3 bytes.

54 bytes

f=lambda s,p=" kMGT":s and f(s[:-3],p[1:])+s[-3:]+p[0]

Try it online!

One potentially-useful observation is that the string format '{:,}'.format puts commas in the places we want to insert letters:

'{:,}'.format(1234567) == "1,234,567"

However, I haven't found a short enough way to use this.

\$\endgroup\$
2
  • \$\begingroup\$ Yep, shortest format version I got so far: 76 bytes \$\endgroup\$
    – movatica
    Commented Oct 13, 2019 at 16:35
  • \$\begingroup\$ The shortest I found using the formatting trick would be: tio.run/… That's already 81 chars, but maybe someone can improve it. \$\endgroup\$
    – Alfe
    Commented Oct 15, 2019 at 13:59
4
\$\begingroup\$

Brachylog, 17 bytes

↔ġ₃;"
kMGT"↔z₀cc↔

Input as a string, returns through output the result, with a trailing newline :)
Explanation:

            More or less a translation of Jelly answer:
↔           Reverse input string
 ġ₃         Group into triples, excluding last group with length in range (0,3]
   ;        Pair those groups with:
    "
kMGT"       A string containing a newline followed by the symbols in reverse order
     ↔      Reverse the pair so the string comes before the list of digit triplets
      z₀    Zip the pair until the smallest list is depleted
        cc  Concatenate the zipped lists together, then concatenate the strings together
          ↔ Reverse the result

Try it online

\$\endgroup\$
3
  • \$\begingroup\$ Looks like we have a new Brachylog user :) \$\endgroup\$
    – Fatalize
    Commented Oct 15, 2019 at 6:52
  • \$\begingroup\$ @Fatalize What gave you that idea? :) You created a nice language, but I wouldn't have found it without that bounty on another question. It'd be harder if I didn't already know a bit of Prolog, of course. \$\endgroup\$ Commented Oct 16, 2019 at 7:09
  • \$\begingroup\$ The idea to create Brachylog? Well I started participating to this site with Prolog, because I though it would be fun and different. Then people started creating new golf languages in the second half of 2015, which gave me the insipiration to make a shorthand version of Prolog \$\endgroup\$
    – Fatalize
    Commented Oct 16, 2019 at 7:11
3
\$\begingroup\$

Perl 5 (-p), 40 bytes

y/ kMG/kMGT/while s/\d\K((\d{3})+$)/ $1/

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Jelly, 14 bytes

13 if input formatted as a string is acceptable for languages which can handle large integers (remove and quote the argument)

ṾṚs3żFtṚʋ“kMGT

A full program accepting the non-negative number as a command line argument which prints the result

Try it online!

How?

ṾṚs3żFtṚʋ“kMGT - Main Link: integer         e.g. 12345
Ṿ              - un-evaluate the integer         ['1', '2', '3', '4', '5']
 Ṛ             - reverse                         ['5', '4', '3', '2', '1']
  s3           - split into chunks of three      [['5', '4', '3'], ['2', '1']]
         “kMGT - literal list of characters      ['k', 'M', 'G', 'T']
        ʋ      - last four links as a dyad  i.e. f([['5', '4', '3'], ['2', '1']], ['k', 'M', 'G', 'T'])
    ż          -   zip                           [[['5', '4', '3'], 'k'], [['2', '1'],'M'], ['G'], ['T']]
     F         -   flatten                       ['5', '4', '3', 'k', '2', '1','M', 'G', 'T']
      t        -   trim (from either side)       ['5', '4', '3', 'k', '2', '1']
       Ṛ       -   reverse                       ['1', '2', 'k', '3', '4', '5']
               - implicit (smashing) print       12k345
\$\endgroup\$
2
\$\begingroup\$

PHP, 67 65 bytes

for(;''<$d=$argn[-++$i];)$r=$d.' kMGT'[$i%3==1?$i/3:5].$r;echo$r;

Try it online!

Loops on input digits from right to left and concatenates each digit to $r variable in reversed order. When we are on 4th, 7th, etc... digit, the respective prefix is concatenated to $r as well. Output has a trailing space.

for(;''<$d=$argn[-++$i];) // loop on input digits from right to left, $d is current digit and $i is incremental (1, 2, ...)
  $r=$d                   // concatenate current digit to beginning of $r
  .' kMGT'                // string of prefixes, first item is space for first digit and causes a trailing space in output
    [$i%3==1?$i/3:5]      // if $i is 1, 4, 7, etc... concatenate letter at index of $i/3 (1/3=0.33, 4/3=1.33, 7/3=2.33, etc...)
                          // PHP uses integer part of decimal indexes, so 1.33 will return the letter at index 1
                          // for any other $i, use index 5 which doesn't exist and is equal to an empty string
  .$r;                    // concatenate $r itself to the end, so everything will be in reversed order of reading (which itself is in reversed order of input)
echo$r;                   // output $r when the loop is finished

A schematic example for how an output is created for input of 1234:

| Current Digit | Concatenation | $r    |
|---------------|---------------|-------|
| 4             | 4             | 4     |
| 3             | 3             | 34    |
| 2             | 2             | 234   |
| 1             | 1k            | 1k234 |
\$\endgroup\$
2
\$\begingroup\$

Icon, 76 71 bytes

procedure f(s)
k:=0
s[i:=*s-2to 2by-3:i]:="kMGT"[k+:=1]&\z
return s
end

Try it online!

Takes input as a string.

\$\endgroup\$
2
\$\begingroup\$

Poetic, 485 bytes

honestly
i lived my life
i faced a boatload of brutal challenges
i desire a day of bliss-i want badly to forget a day where things backfire
i desire a day-i covet a week-and of course i want solitude around everyone
i desire a day-i crave a year-finding no faults i seem to create everyday
i desire a day-i plead,i pray-for an answer i need,i should deserve
fea-r-s,smea-r-s
really,i have a stupid aim
oh world,why was i on earth
o,i suggest venus
o,i deserve a way i go to space
by-by

Try it online!

Poetic is an esolang I made in 2018 for a class project. It's basically brainfuck with word-lengths instead of symbols.

The point of the language is to allow for programs to be written in free-verse poetry. It was fun to write this one (though usually two consecutive ones would be written as 11-letter words; it was hard to golf this poem and make it sound interesting!).

This poem is equivalent to the following brainfuck program, which others are more than welcome to golf:

,[>>->,]<<<<<<<<<<[<++>-----]<+++++<<<<<<<<[<+>---]<--------<<<<<<<<[<+>-------]<--<<<<<<<<[<+>---]<-<<<<<<<-[+[<+<<<]>>>-]>[.>[.[+]]>>]
\$\endgroup\$
2
\$\begingroup\$

JavaScript (Node.js), 55 bytes

f=(n,i=0)=>n.replace(/.+(?=...)/,d=>f(d,i+1)+'kMGT'[i])

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Red, 85 bytes

func[n][p: copy"kMGT"parse reverse n[any[3 skip ahead skip insert(take p)]]reverse n]

Try it online!

Takes the input as a string.

\$\endgroup\$
1
\$\begingroup\$

APL+WIN, 46 bytes

Prompts for integer as a string

∊(n/m),¨(n←3>+/¨' '=¨m←((3/⍳5)⊂¯15↑⎕))/'TGMk '

Try it online! Courtesy of Dyalog Classic

\$\endgroup\$
1
\$\begingroup\$

Gema, 88 characters

\A=@subst{?=\@push\{p\;?\};TGMk }
*=@reverse{@subst{<d3>=\$p\$1\@pop\{p\};@reverse{$0}}}

Boring double reverse approach, but looping in Gema would be painfully long.

Sample run:

bash-5.0$ echo -n '1234567890' | gema '\A=@subst{?=\@push\{p\;?\};TGMk };*=@reverse{@subst{<d3>=\$p\$1\@pop\{p\};@reverse{$0}}}'
1G234M567k890 

Try it online!

\$\endgroup\$
1
\$\begingroup\$

C (clang), 65 62 bytes

f(char*n,z){for(;z--;z%3||printf(L" kMGT"+z/3))putchar(*n++);}

Try it online!

Taking a char array as input.

Saved 3 thanks to @ceilingcat

C (clang), 68 bytes

f(n,s){!s|n&&f(n/10,s+1)+printf(s%3?"%d":"%d%c",n%10," kMGT"[s/3]);}

Try it online!

Taking an int as input

Recursive function which first call itself and outputs after.

f(n,s) // Idk if it's acceptable or if I have to wrap with a g(n){f(n,0);}
{
!s| // to allow 0 value calls
n&& // when n=0 exit recursion 
f(n/10,s+1)+printf(
s%3?"%d":"%d%c" // select format string
,n%10, // digit to print
" kMGT"[s/3]);} // if second format selected add character
\$\endgroup\$
0
1
\$\begingroup\$

PowerShell, 81 73 71 69 64 bytes

($a=$args)|%{$r=$a[--$i]+' kMGT'[$j+5*!$k]+$r;$j+=$k=!($i%3)}
$r

Try it online!

Takes argument as a splatted string.

\$\endgroup\$
1
\$\begingroup\$

Bash, 83 bytes

a=('' k M G T)
s=
(($1>99))&&s=${1: -3}
echo `$0 ${1:0:-3} $[$2+1]`${s:-$1}${a[$2]}

Try it online! (link shows 82 bytes, but uses function recursion instead of $0-recursion)

Nice little recursive solution.

Bash doesn't like invalid string indexing, which both helps (gives us our exit condition for free) and hurts (we have to have separate logic for when our number has n != 0 mod 3 digits).

\$\endgroup\$
1
\$\begingroup\$

Perl 6, 39 bytes

{flip [~] <<''k M G T>>Z~.flip.comb(3)}

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Python 2, 84 74 bytes

i=input();o=""
for x in"kMGT ":o=(i,x+i[-3:])[len(i)>3]+o;i=i[:-3]
print o

Try it online!

Previous version for 84 bytes

i=input()[::-1];o=""
for x in"kMGT ":o+=(i,i[0:3]+x)[len(i)>3];i=i[3:]
print o[::-1]

Try it online!

  • Reverse the input
  • Step through backwards in threes appending prepending the next letter
  • Reverse and print

Not sure that this is the most efficient approach but it's the best I can come up with at the moment.

\$\endgroup\$
0
\$\begingroup\$

JavaScript (V8), 71 bytes

Takes input as number

b=(a,l=10e11,x=0,c=a/l|0)=>(l-1)?(c?c+'TGMk'[x]:'')+b(a%l,l/1000,x+1):c

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Retina 0.8.2, 31 bytes

r`\B...
,$.'$&
T`9630,`TGMk_`,.

Try it online! Explanation:

r`\B...

Match groups of three digits starting from the end but ensure that there is at least one more digit before the match.

,$.'$&

Prefix each match with a marker comma and the number of digits after the match (0 for the last match, then 3, 6 and 9 respectively).

T`9630,`TGMk_`,.

Replace the marker and following number with the appropriate suffix.

\$\endgroup\$
0
\$\begingroup\$

Python 2, 72 bytes

f=lambda n,p=' kMGT':(n>M and f(n/M,p[1:])or'')+`n%M+M`[-3:]+p[0]
M=1000

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ 001G234M567k890 is not the correct output. But I also thought about a recursive solution! \$\endgroup\$
    – Alfe
    Commented Oct 11, 2019 at 23:41
  • \$\begingroup\$ @Alfe: "The result shall be groups of three digits...". 001 is a group of three digits; 1 is not a group of three digits. \$\endgroup\$
    – Chas Brown
    Commented Oct 12, 2019 at 1:50
  • \$\begingroup\$ … as available. The digits 00 in your example are invented and not part of the input. \$\endgroup\$
    – Alfe
    Commented Oct 14, 2019 at 8:32
0
\$\begingroup\$

Python3, 89 83 bytes 65 chars

(or 65 bytes, if you use just ASCII, but I fancy the Unicode superscript small letters)

f=lambda n,p='ᵏᴹᴳᵀ':len(n)<4 and n or f(n[:-3],p[1:])+p[0]+n[-3:]

Try it online!

Python3, 89 bytes:

import re;print(''.join(sum(zip(re.findall('..?.?',str(x)[::-1]),'kMGT.'),()))[::-1][1:])

Try it online!

I didn't try to shorten this dramatically. See it as an incentive to beat me :) This is my first challenge in this forum; by now I understand the asker isn't supposed to post an answer right away. Next time I will wait a little longer before doing it. But since I couldn't really undo the post (deleting the post leaves it visible to too many people anyway), I saw no use in even trying to.

\$\endgroup\$
8
  • 1
    \$\begingroup\$ It is fine to answer your own challenge, but normally, authors wait for a while (I'd say a week) to give others a chance. \$\endgroup\$
    – Adám
    Commented Oct 11, 2019 at 10:53
  • 3
    \$\begingroup\$ Taking input in a variable is not allowed. Typically, answers are either functions with the relevant number of arguments, or full programs using STDIN / STDOUT. For more detailed rules, see Default for Code Golf: Input/Output methods. \$\endgroup\$
    – Arnauld
    Commented Oct 11, 2019 at 11:39
  • \$\begingroup\$ @Alfe, as the maximum length to handle is known, you could just pad the input up to a multiple of 3 ≥ 15, then split to groups from left, without reversing twice: Try it online! Just hint, it still violate the IO rule. \$\endgroup\$
    – manatwork
    Commented Oct 11, 2019 at 12:23
  • \$\begingroup\$ Or the same pad & trim idea without reversing, but with a for loop over hardcoded list: Try it online! \$\endgroup\$
    – manatwork
    Commented Oct 11, 2019 at 12:51
  • 1
    \$\begingroup\$ Your answer doesn't follow your own spec; just leave it at kGMT instead of your fancy Unicode. Especially because the default scoring is bytes not characters. \$\endgroup\$
    – Sanchises
    Commented Oct 13, 2019 at 6:54
0
\$\begingroup\$

Zsh, 53 bytes

a=kMGT
for z y x (${(Oas::)1})s=$x$y$z$a[i++]$s
<<<$s

Try it online!

(s::) splits the stirng, Oa reverses the characters. Zsh arrays and strings are 1-indexed, so $a[0] on the first iteration substitutes nothing.

\$\endgroup\$
0
\$\begingroup\$

Haskell, 99 bytes

f n=concatMap(\(a,b)->a++b)$reverse$zip(reverse<$>chunksOf 3(reverse$show n))(["","k","M","G","T"])

Try it online!

The function f takes a number, converts it into a string and splits it backwards into chunks of 3 characters, e.g.

reverse<$>chunksOf 3(reverse$show 1234) == ["234","1"] 

it then zips the list of chunks with the list of magnitue identifieres ["","k","M","G","T"]. Then the list is reversed and with concatMap converted to one string. It uses the function chunksOf from the package Data.List.Split

\$\endgroup\$
0
\$\begingroup\$

Forth (gforth), 84 bytes

: f s" kMGT" bounds <# do
1000 /mod tuck if
0 # # # d. i c@ hold then loop
0 #s #> ;

Try it online!

A function that takes a number from the stack, and returns a string as (address, length) pair, which is the standard representation for a string in Forth. Produces lots of garbage under the returned string, and a few zeros on stdout.

Forth has a dedicated set of facilities for customized number-to-string formatting, and this challenge seemed to be the right place to showcase it.

On the golfing perspective, I found that do .. if .. then loop (test and ignore the rest of the body) is shorter than begin .. while .. repeat (test and break simple loop) or do .. if leave then .. loop (test and break counted loop).

How it works

: f ( n -- addr len )
  s" kMGT"            \ Create a local string "kMGT"; gives its address and length
  bounds              \ Convert (addr, len) to (addr+8*len, addr)
  <#                  \ Initialize the buffer for number-to-string conversion
  do                  \ Loop; i = addr .. addr+8*len; stack=(n)
    1000 /mod         \   Calculate n%1000 and n/1000
    tuck if           \   Stack=(n/1000 n%1000); if n/1000 is nonzero...
      0 # # # d.      \     emit three digits from n%1000 and discard it
      i c@ hold       \     emit the next char of "kMGT"
    then              \   End if
  loop                \ End loop
  0 #s                \ Emit all the remaining digits
  #>                  \ Finish conversion; push addr/len of the result string
;                     \ End function
\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.